SDL: Add parametrized build script

From 45dfdfbb7b1ac7d13915997c4faa8132187b74e1 Mon Sep 17 00:00:00 2001
From: Anonymous Maarten <[EMAIL REDACTED]>
Date: Sat, 5 Oct 2024 01:32:27 +0200
Subject: [PATCH] Add parametrized build script

[ci skip]
---
 .github/workflows/generic.yml                 |    4 +-
 .github/workflows/release.yml                 |  109 +-
 .gitignore                                    |    1 +
 CMakeLists.txt                                |   35 +-
 Xcode/SDL/SDL.xcodeproj/project.pbxproj       |    2 +-
 Xcode/SDL/pkg-support/build.xcconfig          |    8 +
 .../SDL3Config.cmake}                         |    0
 .../SDL3ConfigVersion.cmake}                  |    0
 .../cmake/SDL3/SDL3Config.cmake}              |   24 +-
 .../cmake/SDL3/SDL3ConfigVersion.cmake}       |    0
 build-scripts/build-release.py                | 1642 +++++++++++------
 build-scripts/create-android-project.py       |    5 +-
 build-scripts/create-release.py               |   43 +
 build-scripts/create-release.sh               |    6 -
 .../pkg-support/android/INSTALL.md.in         |  119 +-
 .../pkg-support/android/__main__.py.in        |   14 +-
 ...rsion.cmake => SDL3ConfigVersion.cmake.in} |   25 +-
 .../pkg-support/android/description.json.in   |    5 +
 build-scripts/pkg-support/mingw/Makefile      |    2 +
 .../pkg-support/msvc/Directory.Build.props    |    8 +
 .../msvc/cmake/SDL3Config.cmake.in            |    6 +-
 .../msvc/cmake/SDL3ConfigVersion.cmake.in     |   25 +-
 .../source/SDL_revision.h.cmake.in            |   41 +
 .../pkg-support/source/SDL_revision.h.in      |   56 +
 build-scripts/release-info.json               |  215 +++
 build-scripts/updaterev.sh                    |    1 -
 cmake/android/FindSdlAndroidPlatform.cmake    |    5 +-
 cmake/sdlcompilers.cmake                      |    2 +-
 cmake/sdlcpu.cmake                            |  304 +--
 docs/README-android.md                        |   28 +-
 include/build_config/SDL_revision.h.cmake     |   16 +-
 31 files changed, 1822 insertions(+), 929 deletions(-)
 create mode 100644 Xcode/SDL/pkg-support/build.xcconfig
 rename Xcode/SDL/pkg-support/resources/{CMake/sdl3-config.cmake => cmake/SDL3Config.cmake} (100%)
 rename Xcode/SDL/pkg-support/resources/{CMake/sdl3-config-version.cmake => cmake/SDL3ConfigVersion.cmake} (100%)
 rename Xcode/SDL/pkg-support/{resources/share/cmake/SDL3/sdl3-config.cmake => share/cmake/SDL3/SDL3Config.cmake} (87%)
 rename Xcode/SDL/pkg-support/{resources/share/cmake/SDL3/sdl3-config-version.cmake => share/cmake/SDL3/SDL3ConfigVersion.cmake} (100%)
 create mode 100755 build-scripts/create-release.py
 delete mode 100755 build-scripts/create-release.sh
 rename build-scripts/pkg-support/android/cmake/{SDL3ConfigVersion.cmake => SDL3ConfigVersion.cmake.in} (55%)
 create mode 100644 build-scripts/pkg-support/android/description.json.in
 create mode 100644 build-scripts/pkg-support/msvc/Directory.Build.props
 rename VisualC/pkg-support/cmake/sdl3-config.cmake => build-scripts/pkg-support/msvc/cmake/SDL3Config.cmake.in (95%)
 rename VisualC/pkg-support/cmake/sdl3-config-version.cmake => build-scripts/pkg-support/msvc/cmake/SDL3ConfigVersion.cmake.in (55%)
 create mode 100644 build-scripts/pkg-support/source/SDL_revision.h.cmake.in
 create mode 100644 build-scripts/pkg-support/source/SDL_revision.h.in
 create mode 100644 build-scripts/release-info.json

diff --git a/.github/workflows/generic.yml b/.github/workflows/generic.yml
index 70f2b9b1fbdb6..5c17a4aea3f84 100644
--- a/.github/workflows/generic.yml
+++ b/.github/workflows/generic.yml
@@ -191,9 +191,9 @@ jobs:
         run: |
           echo "This should show us the SDL_REVISION"
           echo "Shared library:"
-          ${{ (matrix.platform.shared-lib && format('strings build/{0} | grep SDL-', matrix.platform.shared-lib)) || 'echo "<Shared library not supported by platform>"' }}
+          ${{ (matrix.platform.shared-lib && format('strings build/{0} | grep "Github Workflow"', matrix.platform.shared-lib)) || 'echo "<Shared library not supported by platform>"' }}
           echo "Static library:"
-          ${{ (matrix.platform.static-lib && format('strings build/{0} | grep SDL-', matrix.platform.static-lib)) || 'echo "<Static library not supported by platform>"' }}
+          ${{ (matrix.platform.static-lib && format('strings build/{0} | grep "Github Workflow"', matrix.platform.static-lib)) || 'echo "<Static library not supported by platform>"' }}
       - name: 'Run build-time tests (CMake)'
         id: tests
         if: ${{ !matrix.platform.no-cmake && matrix.platform.run-tests }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 388f3806b7c45..2ce85e5366a44 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -37,9 +37,8 @@ jobs:
         shell: bash
         run: |
           python build-scripts/build-release.py \
-            --create source \
+            --actions source \
             --commit ${{ inputs.commit }} \
-            --project SDL3 \
             --root "${{ github.workspace }}/SDL" \
             --github \
             --debug
@@ -93,7 +92,7 @@ jobs:
       - name: 'Set up Python'
         uses: actions/setup-python@v5
         with:
-          python-version: '3.10'
+          python-version: '3.11'
       - name: 'Fetch build-release.py'
         uses: actions/checkout@v4
         with:
@@ -114,9 +113,8 @@ jobs:
         shell: bash
         run: |
           python build-scripts/build-release.py \
-            --create xcframework \
+            --actions dmg \
             --commit ${{ inputs.commit }} \
-            --project SDL3 \
             --root "${{ steps.tar.outputs.path }}" \
             --github \
             --debug
@@ -240,12 +238,13 @@ jobs:
     outputs:
       VC-x86: ${{ steps.releaser.outputs.VC-x86 }}
       VC-x64: ${{ steps.releaser.outputs.VC-x64 }}
+      VC-arm64: ${{ steps.releaser.outputs.VC-arm64 }}
       VC-devel: ${{ steps.releaser.outputs.VC-devel }}
     steps:
       - name: 'Set up Python'
         uses: actions/setup-python@v5
         with:
-          python-version: '3.10'
+          python-version: '3.11'
       - name: 'Fetch build-release.py'
         uses: actions/checkout@v4
         with:
@@ -265,12 +264,11 @@ jobs:
       - name: 'Build MSVC binary archives'
         id: releaser
         run: |
-          python build-scripts/build-release.py     `
-            --create win32                          `
-            --commit ${{ inputs.commit }}           `
-            --project SDL3                          `
-            --root "${{ steps.zip.outputs.path }}"  `
-            --github                                `
+          python build-scripts/build-release.py `
+            --actions msvc `
+            --commit ${{ inputs.commit }} `
+            --root "${{ steps.zip.outputs.path }}" `
+            --github `
             --debug
       - name: 'Store MSVC archives'
         uses: actions/upload-artifact@v4
@@ -405,7 +403,7 @@ jobs:
       - name: 'Set up Python'
         uses: actions/setup-python@v5
         with:
-          python-version: '3.10'
+          python-version: '3.11'
       - name: 'Fetch build-release.py'
         uses: actions/checkout@v4
         with:
@@ -428,12 +426,11 @@ jobs:
       - name: 'Build MinGW binary archives'
         id: releaser
         run: |
-          python build-scripts/build-release.py     \
-            --create mingw                          \
-            --commit ${{ inputs.commit }}           \
-            --project SDL3                          \
-            --root "${{ steps.tar.outputs.path }}"  \
-            --github                                \
+          python build-scripts/build-release.py \
+            --actions mingw \
+            --commit ${{ inputs.commit }} \
+            --root "${{ steps.tar.outputs.path }}" \
+            --github \
             --debug
       - name: 'Store MinGW archives'
         uses: actions/upload-artifact@v4
@@ -507,7 +504,7 @@ jobs:
       - name: 'Set up Python'
         uses: actions/setup-python@v5
         with:
-          python-version: '3.10'
+          python-version: '3.11'
       - name: 'Fetch build-release.py'
         uses: actions/checkout@v4
         with:
@@ -540,12 +537,11 @@ jobs:
       - name: 'Build Android prefab binary archive(s)'
         id: releaser
         run: |
-          python build-scripts/build-release.py     \
-            --create android                        \
-            --commit ${{ inputs.commit }}           \
-            --project SDL3                          \
-            --root "${{ steps.tar.outputs.path }}"  \
-            --github                                \
+          python build-scripts/build-release.py \
+            --actions android \
+            --commit ${{ inputs.commit }} \
+            --root "${{ steps.tar.outputs.path }}" \
+            --github \
             --debug
       - name: 'Store Android archive(s)'
         uses: actions/upload-artifact@v4
@@ -560,7 +556,7 @@ jobs:
       - name: 'Set up Python'
         uses: actions/setup-python@v5
         with:
-          python-version: '3.10'
+          python-version: '3.11'
       - uses: actions/setup-java@v4
         with:
           distribution: 'temurin'
@@ -581,6 +577,29 @@ jobs:
           mkdir -p /tmp/tardir
           tar -C /tmp/tardir -v -x -f "${{ github.workspace }}/${{ needs.src.outputs.src-tar-gz }}"
           echo "path=/tmp/tardir/${{ needs.src.outputs.project }}-${{ needs.src.outputs.version }}" >>$GITHUB_OUTPUT
+      - name: 'Extract Android SDK from AAR'
+        id: sdk
+        run: |
+          python "${{ github.workspace }}/${{ needs.android.outputs.android-aar }}" -o /tmp/SDL3-android
+          echo "prefix=/tmp/SDL3-android" >>$GITHUB_OUTPUT
+      - name: 'CMake (configure + build) x86, x64, arm32, arm64'
+        run: |
+          android_abis="x86 x86_64 armeabi-v7a arm64-v8a"
+          for android_abi in ${android_abis}; do
+            echo "Configuring ${android_abi}..."
+            cmake -S "${{ steps.src.outputs.path }}/cmake/test" \
+              -DTEST_FULL=TRUE \
+              -DTEST_STATIC=FALSE \
+              -DTEST_TEST=TRUE \
+              -DCMAKE_PREFIX_PATH="${{ steps.sdk.outputs.prefix }}" \
+              -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK_HOME}/build/cmake/android.toolchain.cmake \
+              -DANDROID_ABI=${android_abi} \
+              -Werror=dev \
+              -DCMAKE_BUILD_TYPE=Release \
+              -B "${android_abi}"
+            echo "Building ${android_abi}..."
+            cmake --build "${android_abi}" --config Release --verbose
+          done
       - name: 'Create gradle project'
         id: create-gradle-project
         run: |
@@ -591,11 +610,6 @@ jobs:
             --variant aar \
             --output "/tmp/projects"
           echo "path=/tmp/projects/org.libsdl.testspriteminimal" >>$GITHUB_OUTPUT
-
-          echo ""
-          echo "Project contents:"
-          echo ""
-          find "/tmp/projects/org.libsdl.testspriteminimal"
       - name: 'Copy SDL3 aar into Gradle project'
         run: |
           cp "${{ github.workspace }}/${{ needs.android.outputs.android-aar }}" "${{ steps.create-gradle-project.outputs.path }}/app/libs"
@@ -604,34 +618,11 @@ jobs:
           echo "Project contents:"
           echo ""
           find "${{ steps.create-gradle-project.outputs.path }}"
-      - name: 'Build app (Gradle & ndk-build)'
-        run: |
-          cd "${{ steps.create-gradle-project.outputs.path }}"
-          ./gradlew -i assembleRelease -PBUILD_WITH_CMAKE=1
       - name: 'Build app (Gradle & CMake)'
         run: |
           cd "${{ steps.create-gradle-project.outputs.path }}"
-          ./gradlew -i assembleRelease
-      - name: 'Extract Android SDK from AAR'
-        id: sdk
-        run: |
-          python "${{ github.workspace }}/${{ needs.android.outputs.android-aar }}" -o /tmp/SDL3-android
-          echo "prefix=/tmp/SDL3-android" >>$GITHUB_OUTPUT
-      - name: 'CMake (configure + build) x86, x64, arm32, arm64'
+          ./gradlew -i assembleRelease -Pandroid.native.buildOutput=verbose -PBUILD_WITH_CMAKE=1
+      - name: 'Build app (Gradle & ndk-build)'
         run: |
-          android_abis="x86 x86_64 armeabi-v7a arm64-v8a"
-          for android_abi in ${android_abis}; do
-            echo "Configuring ${android_abi}..."
-            cmake -S "${{ steps.src.outputs.path }}/cmake/test"                               \
-              -DTEST_FULL=TRUE                                                                \
-              -DTEST_STATIC=FALSE                                                             \
-              -DTEST_TEST=TRUE                                                                \
-              -DCMAKE_PREFIX_PATH="${{ steps.sdk.outputs.prefix }}"                           \
-              -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK_HOME}/build/cmake/android.toolchain.cmake  \
-              -DANDROID_ABI=${android_abi}                                                    \
-              -Werror=dev                                                                     \
-              -DCMAKE_BUILD_TYPE=Release                                                      \
-              -B "${android_abi}"
-            echo "Building ${android_abi}..."
-            cmake --build "${android_abi}" --config Release --verbose
-          done
+          cd "${{ steps.create-gradle-project.outputs.path }}"
+          ./gradlew -i assembleRelease -Pandroid.native.buildOutput=verbose
diff --git a/.gitignore b/.gitignore
index 3c45e91b757d3..a92f38693ec5c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -51,6 +51,7 @@ cmake-build-*
 .DS_Store
 xcuserdata
 *.xcworkspace
+Xcode/build.xcconfig
 
 # for Visual Studio Code
 .vscode/
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8eebc523ebc40..063a427f4cae0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1415,13 +1415,20 @@ if(ANDROID)
         set(javadoc_index_html "${javadocdir}/index.html")
         add_custom_command(
           OUTPUT "${javadoc_index_html}"
-          COMMAND ${CMAKE_COMMAND} -E rm -rf "${javadocdir}"
+          COMMAND ${CMAKE_COMMAND} -E rm -rf "${javadocdir}" "${javadocjar}"
           COMMAND ${Java_JAVADOC_EXECUTABLE} -encoding utf8 -d "${javadocdir}"
             -classpath "${path_android_jar}"
             -author -use -version ${SDL_JAVA_SOURCES}
           DEPENDS ${SDL_JAVA_SOURCES} "${path_android_jar}"
         )
-        add_custom_target(SDL3-javadoc ALL DEPENDS "${javadoc_index_html}")
+        add_custom_command(
+          OUTPUT "${javadocjar}"
+          COMMAND ${Java_JAR_EXECUTABLE} -c -f "${javadocjar}"
+            -C "${javadocdir}" *
+          WORKING_DIRECTORY "${javadocdir}"
+          DEPENDS ${javadoc_index_html}
+        )
+        add_custom_target(SDL3-javadoc ALL DEPENDS "${javadoc_index_html}" "${javadocjar}")
         set_property(TARGET SDL3-javadoc PROPERTY OUTPUT_DIR "${javadocdir}")
       endif()
     endif()
@@ -3044,21 +3051,17 @@ foreach(_hdr IN LISTS SDL3_INCLUDE_FILES)
   endif()
 endforeach()
 
-set(SDL_REVISION "" CACHE STRING "Custom SDL revision (overrides SDL_REVISION_SUFFIX)")
-if(NOT SDL_REVISION)
-  set(SDL_REVISION_SUFFIX "" CACHE STRING "Suffix for the SDL revision")
-  if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/VERSION.txt")
-    # If VERSION.txt exists, it contains the SDL version
-    file(READ "${CMAKE_CURRENT_SOURCE_DIR}/VERSION.txt" SDL_REVISION_CENTER)
-    string(STRIP "${SDL_REVISION_CENTER}" SDL_REVISION_CENTER)
+# If REVISION.txt exists, then we are building from a SDL release.
+# SDL_revision.h(.cmake) in source releases have SDL_REVISION baked into them.
+if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/REVISION.txt")
+  set(SDL_REVISION "" CACHE STRING "Custom SDL revision")
+  if(SDL_REVISION)
+    set(SDL_REVISION_CENTER "${SDL_VERSION_MAJOR}.${SDL_VERSION_MINOR}.${SDL_VERSION_MICRO}-${SDL_REVISION}")
   else()
-    # If VERSION does not exist, use git to calculate a version
+    # If SDL_REVISION is not overrided, use git to describe
     git_describe(SDL_REVISION_CENTER)
-    if(NOT SDL_REVISION_CENTER)
-      set(SDL_REVISION_CENTER "${SDL3_VERSION}-no-vcs")
-    endif()
   endif()
-  set(SDL_REVISION "SDL-${SDL_REVISION_CENTER}${SDL_REVISION_SUFFIX}")
+  set(SDL_REVISION "SDL3-${SDL_REVISION_CENTER}")
 endif()
 
 execute_process(COMMAND "${CMAKE_COMMAND}" -E make_directory "${SDL3_BINARY_DIR}/include/SDL3")
@@ -3645,7 +3648,7 @@ if(NOT SDL_DISABLE_INSTALL)
       )
     endif()
     if(TARGET SDL3-javasources)
-      install(FILES  "${SDL3_BINARY_DIR}/SDL3-${SDL3_VERSION}-sources.jar"
+      install(FILES "${SDL3_BINARY_DIR}/SDL3-${SDL3_VERSION}-sources.jar"
         DESTINATION "${SDL_INSTALL_JAVADIR}/SDL3")
     endif()
   endif()
@@ -3659,7 +3662,7 @@ if(NOT SDL_DISABLE_INSTALL)
     )
     if(TARGET SDL3-javadoc)
       set(SDL_INSTALL_JAVADOCDIR "${CMAKE_INSTALL_DATAROOTDIR}/javadoc" CACHE PATH "Path where to install SDL3 javadoc")
-      install(DIRECTORY "${SDL3_BINARY_DIR}/docs/javadoc/"
+      install(FILES "${SDL3_BINARY_DIR}/SDL3-${SDL3_VERSION}-javadoc.jar"
         DESTINATION "${SDL_INSTALL_JAVADOCDIR}/SDL3")
     endif()
   endif()
diff --git a/Xcode/SDL/SDL.xcodeproj/project.pbxproj b/Xcode/SDL/SDL.xcodeproj/project.pbxproj
index 9adffb7ea5509..bfd2584dcb3a6 100644
--- a/Xcode/SDL/SDL.xcodeproj/project.pbxproj
+++ b/Xcode/SDL/SDL.xcodeproj/project.pbxproj
@@ -2795,7 +2795,7 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
-			shellScript = "set -ex\n\nmkdir -p build/dmg-tmp/share/cmake/SDL3\ncp -a build/SDL3.xcframework build/dmg-tmp/\n\ncp pkg-support/resources/License.txt build/dmg-tmp\ncp pkg-support/resources/ReadMe.txt build/dmg-tmp\ncp pkg-support/resources/share/cmake/SDL3/sdl3-config.cmake build/dmg-tmp/share/cmake/SDL3\ncp pkg-support/resources/share/cmake/SDL3/sdl3-config-version.cmake build/dmg-tmp/share/cmake/SDL3\n\n# remove the .DS_Store files if any (we may want to provide one in the future for fancy .dmgs)\nfind build/dmg-tmp -name .DS_Store -exec rm -f \"{}\" \\;\n\n# for fancy .dmg\nmkdir -p build/dmg-tmp/.logo\ncp pkg-support/resources/SDL_DS_Store build/dmg-tmp/.DS_Store\ncp pkg-support/sdl_logo.pdf build/dmg-tmp/.logo\n\n# create the dmg\nhdiutil create -ov -fs HFS+ -volname SDL3 -srcfolder build/dmg-tmp build/SDL3.dmg\n\n# clean up\nrm -rf build/dmg-tmp\n";
+			shellScript = "set -ex\n\nmkdir -p build/dmg-tmp/share/cmake/SDL3\ncp -a build/SDL3.xcframework build/dmg-tmp/\n\ncp pkg-support/resources/License.txt build/dmg-tmp\ncp pkg-support/resources/ReadMe.txt build/dmg-tmp\ncp pkg-support/share/cmake/SDL3/SDL3Config.cmake build/dmg-tmp/share/cmake/SDL3\ncp pkg-support/share/cmake/SDL3/SDL3ConfigVersion.cmake build/dmg-tmp/share/cmake/SDL3\n\n# remove the .DS_Store files if any (we may want to provide one in the future for fancy .dmgs)\nfind build/dmg-tmp -name .DS_Store -exec rm -f \"{}\" \\;\n\n# for fancy .dmg\nmkdir -p build/dmg-tmp/.logo\ncp pkg-support/resources/SDL_DS_Store build/dmg-tmp/.DS_Store\ncp pkg-support/sdl_logo.pdf build/dmg-tmp/.logo\n\n# create the dmg\nhdiutil create -ov -fs HFS+ -volname SDL3 -srcfolder build/dmg-tmp build/SDL3.dmg\n\n# clean up\nrm -rf build/dmg-tmp\n";
 		};
 		F3B38CF0296F63D1005DA6D3 /* ShellScript */ = {
 			isa = PBXShellScriptBuildPhase;
diff --git a/Xcode/SDL/pkg-support/build.xcconfig b/Xcode/SDL/pkg-support/build.xcconfig
new file mode 100644
index 0000000000000..6541759ec8230
--- /dev/null
+++ b/Xcode/SDL/pkg-support/build.xcconfig
@@ -0,0 +1,8 @@
+//
+//  build.xcconfig
+//
+
+// Configuration settings file format documentation can be found at:
+// https://help.apple.com/xcode/#/dev745c5c974
+
+SDL_PREPROCESSOR_DEFINITIONS = SDL_VENDOR_INFO=\"libsdl.org\"
diff --git a/Xcode/SDL/pkg-support/resources/CMake/sdl3-config.cmake b/Xcode/SDL/pkg-support/resources/cmake/SDL3Config.cmake
similarity index 100%
rename from Xcode/SDL/pkg-support/resources/CMake/sdl3-config.cmake
rename to Xcode/SDL/pkg-support/resources/cmake/SDL3Config.cmake
diff --git a/Xcode/SDL/pkg-support/resources/CMake/sdl3-config-version.cmake b/Xcode/SDL/pkg-support/resources/cmake/SDL3ConfigVersion.cmake
similarity index 100%
rename from Xcode/SDL/pkg-support/resources/CMake/sdl3-config-version.cmake
rename to Xcode/SDL/pkg-support/resources/cmake/SDL3ConfigVersion.cmake
diff --git a/Xcode/SDL/pkg-support/resources/share/cmake/SDL3/sdl3-config.cmake b/Xcode/SDL/pkg-support/share/cmake/SDL3/SDL3Config.cmake
similarity index 87%
rename from Xcode/SDL/pkg-support/resources/share/cmake/SDL3/sdl3-config.cmake
rename to Xcode/SDL/pkg-support/share/cmake/SDL3/SDL3Config.cmake
index 278cd3b20293c..9d29aae9a3fb3 100644
--- a/Xcode/SDL/pkg-support/resources/share/cmake/SDL3/sdl3-config.cmake
+++ b/Xcode/SDL/pkg-support/share/cmake/SDL3/SDL3Config.cmake
@@ -32,14 +32,24 @@ endmacro()
 set(SDL3_FOUND TRUE)
 
 macro(_check_target_is_simulator)
-    include(CheckCSourceCompiles)
-    check_c_source_compiles([===[
+    set(src [===[
     #include <TargetConditionals.h>
-    #if defined(TARGET_OS_SIMULATOR)
+    #if defined(TARGET_OS_SIMULATOR) && TARGET_OS_SIMULATOR
     int target_is_simulator;
     #endif
     int main(int argc, char *argv[]) { return target_is_simulator; }
-    ]===] SDL_TARGET_IS_SIMULATOR)
+    ]===])
+    if(CMAKE_C_COMPILER)
+        include(CheckCSourceCompiles)
+        check_c_source_compiles("${src}" SDL_TARGET_IS_SIMULATOR)
+    elseif(CMAKE_CXX_COMPILER)
+        include(CheckCXXSourceCompiles)
+        check_cxx_source_compiles("${src}" SDL_TARGET_IS_SIMULATOR)
+    else()
+        enable_language(C)
+        include(CheckCSourceCompiles)
+        check_c_source_compiles("${src}" SDL_TARGET_IS_SIMULATOR)
+    endif()
 endmacro()
 
 if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
@@ -59,7 +69,7 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "tvOS")
 elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
     set(_xcfw_target_subdir "macos-arm64_x86_64")
 else()
-    message(WARNING "Unsupported Apple platform (${CMAKE_SYSTEM_NAME}) and broken sdl3-config-version.cmake")
+    message(WARNING "Unsupported Apple platform (${CMAKE_SYSTEM_NAME}) and broken SDL3ConfigVersion.cmake")
     set(SDL3_FOUND FALSE)
     return()
 endif()
@@ -89,7 +99,9 @@ set(SDL3_Headers_FOUND TRUE)
 
 if(NOT TARGET SDL3::SDL3-shared)
     add_library(SDL3::SDL3-shared SHARED IMPORTED)
-    if(CMAKE_VERSION GREATER_EQUAL "3.28")
+    # CMake does not automatically add RPATHS when using xcframeworks
+    # https://gitlab.kitware.com/cmake/cmake/-/issues/25998
+    if(0)  # if(CMAKE_VERSION GREATER_EQUAL "3.28")
         set_target_properties(SDL3::SDL3-shared
             PROPERTIES
                 FRAMEWORK "TRUE"
diff --git a/Xcode/SDL/pkg-support/resources/share/cmake/SDL3/sdl3-config-version.cmake b/Xcode/SDL/pkg-support/share/cmake/SDL3/SDL3ConfigVersion.cmake
similarity index 100%
rename from Xcode/SDL/pkg-support/resources/share/cmake/SDL3/sdl3-config-version.cmake
rename to Xcode/SDL/pkg-support/share/cmake/SDL3/SDL3ConfigVersion.cmake
diff --git a/build-scripts/build-release.py b/build-scripts/build-release.py
index e84dcd5fd2ea4..5ef2a3487ae57 100755
--- a/build-scripts/build-release.py
+++ b/build-scripts/build-release.py
@@ -1,17 +1,28 @@
 #!/usr/bin/env python
 
+"""
+This script is shared between SDL2, SDL3, and all satellite libraries.
+Don't specialize this script for doing project-specific modifications.
+Rather, modify release-info.json.
+"""
+
 import argparse
 import collections
+import dataclasses
+from collections.abc import Callable
 import contextlib
 import datetime
+import fnmatch
 import glob
 import io
 import json
 import logging
+import multiprocessing
 import os
 from pathlib import Path
 import platform
 import re
+import shlex
 import shutil
 import subprocess
 import sys
@@ -21,21 +32,55 @@
 import typing
 import zipfile
 
-logger = logging.getLogger(__name__)
-
 
-VcArchDevel = collections.namedtuple("VcArchDevel", ("dll", "pdb", "imp", "test"))
+logger = logging.getLogger(__name__)
 GIT_HASH_FILENAME = ".git-hash"
-
-ANDROID_AVAILABLE_ABIS = [
-    "armeabi-v7a",
-    "arm64-v8a",
-    "x86",
-    "x86_64",
-]
-ANDROID_MINIMUM_API = 19
-ANDROID_TARGET_API = 29
-ANDROID_MINIMUM_NDK = 21
+REVISION_TXT = "REVISION.txt"
+
+
+def safe_isotime_to_datetime(str_isotime: str) -> datetime.datetime:
+    try:
+        return datetime.datetime.fromisoformat(str_isotime)
+    except ValueError:
+        pass
+    logger.warning("Invalid iso time: %s", str_isotime)
+    if str_isotime[-6:-5] in ("+", "-"):
+        # Commits can have isotime with invalid timezone offset (e.g. "2021-07-04T20:01:40+32:00")
+        modified_str_isotime = str_isotime[:-6] + "+00:00"
+        try:
+            return datetime.datetime.fromisoformat(modified_str_isotime)
+        except ValueError:
+            pass
+    raise ValueError(f"Invalid isotime: {str_isotime}")
+
+
+def arc_join(*parts: list[str]) -> str:
+    assert all(p[:1] != "/" and p[-1:] != "/" for p in parts), f"None of {parts} may start or end with '/'"
+    return "/".join(p for p in parts if p)
+
+
+@dataclasses.dataclass(frozen=True)
+class VsArchPlatformConfig:
+    arch: str
+    configuration: str
+    platform: str
+
+    def extra_context(self):
+        return {
+            "ARCH": self.arch,
+            "CONFIGURATION": self.configuration,
+            "PLATFORM": self.platform,
+        }
+
+
+@contextlib.contextmanager
+def chdir(path):
+    original_cwd = os.getcwd()
+    try:
+        os.chdir(path)
+        yield
+    finally:
+        os.chdir(original_cwd)
 
 
 class Executer:
@@ -43,14 +88,18 @@ def __init__(self, root: Path, dry: bool=False):
         self.root = root
         self.dry = dry
 
-    def run(self, cmd, stdout=False, dry_out=None, force=False):
+    def run(self, cmd, cwd=None, env=None):
+        logger.info("Executing args=%r", cmd)
         sys.stdout.flush()
+        if not self.dry:
+            subprocess.check_call(cmd, cwd=cwd or self.root, env=env, text=True)
+
+    def check_output(self, cmd, cwd=None, dry_out=None, env=None, text=True):
         logger.info("Executing args=%r", cmd)
-        if self.dry and not force:
-            if stdout:
-                return subprocess.run(["echo", "-E", dry_out or ""], stdout=subprocess.PIPE if stdout else None, text=True, check=True, cwd=self.root)
-        else:
-            return subprocess.run(cmd, stdout=subprocess.PIPE if stdout else None, text=True, check=True, cwd=self.root)
+        sys.stdout.flush()
+        if self.dry:
+            return dry_out
+        return subprocess.check_output(cmd, cwd=cwd or self.root, env=env, text=text)
 
 
 class SectionPrinter:
@@ -103,7 +152,7 @@ def find_vsdevcmd(self, year: typing.Optional[str]=None) -> typing.Optional[Path
                 return None
             vswhere_spec.extend(["-version", f"[{version},{version+1})"])
         vswhere_cmd = ["vswhere"] + vswhere_spec + ["-property", "installationPath"]
-        vs_install_path = Path(self.executer.run(vswhere_cmd, stdout=True, dry_out="/tmp").stdout.strip())
+        vs_install_path = Path(self.executer.check_output(vswhere_cmd, dry_out="/tmp").strip())
         logger.info("VS install_path = %s", vs_install_path)
         assert vs_install_path.is_dir(), "VS installation path does not exist"
         vsdevcmd_path = vs_install_path / "Common7/Tools/vsdevcmd.bat"
@@ -116,7 +165,7 @@ def find_vsdevcmd(self, year: typing.Optional[str]=None) -> typing.Optional[Path
 
     def find_msbuild(self) -> typing.Optional[Path]:
         vswhere_cmd = ["vswhere", "-latest", "-requires", "Microsoft.Component.MSBuild", "-find", r"MSBuild\**\Bin\MSBuild.exe"]
-        msbuild_path = Path(self.executer.run(vswhere_cmd, stdout=True, dry_out="/tmp/MSBuild.exe").stdout.strip())
+        msbuild_path = Path(self.executer.check_output(vswhere_cmd, dry_out="/tmp/MSBuild.exe").strip())
         logger.info("MSBuild path = %s", msbuild_path)
         if self.dry:
             msbuild_path.parent.mkdir(parents=True, exist_ok=True)
@@ -124,11 +173,11 @@ def find_msbuild(self) -> typing.Optional[Path]:
         assert msbuild_path.is_file(), "MSBuild.exe does not exist"
         return msbuild_path
 
-    def build(self, arch: str, platform: str, configuration: str, projects: list[Path]):
+    def build(self, arch_platform: VsArchPlatformConfig, projects: list[Path]):
         assert projects, "Need at least one project to build"
 
-        vsdev_cmd_str = f"\"{self.vsdevcmd}\" -arch={arch}"
-        msbuild_cmd_str = " && ".join([f"\"{self.msbuild}\" \"{project}\" /m /p:BuildInParallel=true /p:Platform={platform} /p:Configuration={configuration}" for project in projects])
+        vsdev_cmd_str = f"\"{self.vsdevcmd}\" -arch={arch_platform.arch}"
+        msbuild_cmd_str = " && ".join([f"\"{self.msbuild}\" \"{project}\" /m /p:BuildInParallel=true /p:Platform={arch_platform.platform} /p:Configuration={arch_platform.configuration}" for project in projects])
         bat_contents = f"{vsdev_cmd_str} && {msbuild_cmd_str}\n"
         bat_path = Path(tempfile.gettempdir()) / "cmd.bat"
         with bat_path.open("w") as f:
@@ -139,35 +188,308 @@ def build(self, arch: str, platform: str, configuration: str, projects: list[Pat
         self.executer.run(cmd)
 
 
-class Releaser:
-    def __init__(self, project: str, commit: str, root: Path, dist_path: Path, section_printer: SectionPrinter, executer: Executer, cmake_generator: str):
-        self.project = project
-        self.version = self.extract_sdl_version(root=root, project=project)
+class Archiver:
+    def __init__(self, zip_path: typing.Optional[Path]=None, tgz_path: typing.Optional[Path]=None, txz_path: typing.Optional[Path]=None):
+        self._zip_files = []
+        self._tar_files = []
+        self._added_files = set()
+        if zip_path:
+            self._zip_files.append(zipfile.ZipFile(zip_path, "w", compression=zipfile.ZIP_DEFLATED))
+        if tgz_path:
+            self._tar_files.append(tarfile.open(tgz_path, "w:gz"))
+        if txz_path:
+            self._tar_files.append(tarfile.open(txz_path, "w:xz"))
+
+    @property
+    def added_files(self) -> set[str]:
+        return self._added_files
+
+    def add_file_data(self, arcpath: str, data: bytes, mode: int, time: datetime.datetime):
+        for zf in self._zip_files:
+            file_data_time = (time.year, time.month, time.day, time.hour, time.minute, time.second)
+            zip_info = zipfile.ZipInfo(filename=arcpath, date_time=file_data_time)
+            zip_info.external_attr = mode << 16
+            zip_info.compress_type = zipfile.ZIP_DEFLATED
+            zf.writestr(zip_info, data=data)
+        for tf in self._tar_files:
+            tar_info = tarfile.TarInfo(arcpath)
+            tar_info.type = tarfile.REGTYPE
+            tar_info.mode = mode
+            tar_info.size = len(data)
+            tar_info.mtime = int(time.timestamp())
+            tf.addfile(tar_info, fileobj=io.BytesIO(data))
+
+        self._added_files.add(arcpath)
+
+    def add_symlink(self, arcpath: str, target: str, time: datetime.datetime, files_for_zip):
+        logger.debug("Adding symlink (target=%r) -> %s", target, arcpath)
+        for zf in self._zip_files:
+            file_data_time = (time.year, time.month, time.day, time.hour, time.minute, time.second)
+            for f in files_for_zip:
+                zip_info = zipfile.ZipInfo(filename=f["arcpath"], date_time=file_data_time)
+                zip_info.external_attr = f["mode"] << 16
+                zip_info.compress_type = zipfile.ZIP_DEFLATE

(Patch may be truncated, please check the link at the top of this post.)