SDL_ttf: cmake: support creating binary packages

From 4823dcea69e85dd348cd6ace154852874e0566d7 Mon Sep 17 00:00:00 2001
From: Anonymous Maarten <[EMAIL REDACTED]>
Date: Thu, 23 Jan 2025 15:24:32 +0100
Subject: [PATCH] cmake: support creating binary packages

---
 .github/workflows/main.yml        |  38 +++++++----
 CMakeLists.txt                    |  26 +++++++-
 cmake/CPackProjectConfig.cmake.in |  37 +++++++++++
 cmake/sdlplatform.cmake           | 106 ++++++++++++++++++++++++++++++
 4 files changed, 191 insertions(+), 16 deletions(-)
 create mode 100644 cmake/CPackProjectConfig.cmake.in
 create mode 100644 cmake/sdlplatform.cmake

diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 04b453f0..21c5fad3 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -15,12 +15,12 @@ jobs:
       fail-fast: false
       matrix:
         platform:
-        - { name: Windows (MSVC+CMake),         os: windows-latest, shell: sh,   cmake: '-DPerl_ROOT=C:/Strawberry/perl/bin/ -DSDLTTF_VENDORED=ON -GNinja', msvc: 1, shared: 1, static: 0 }
+        - { name: Windows (MSVC+CMake),         os: windows-latest, shell: sh,   cmake: '-DPerl_ROOT=C:/Strawberry/perl/bin/ -DSDLTTF_VENDORED=ON -GNinja', msvc: 1, shared: 1, static: 0, artifact: 'SDL3_ttf-VC-x64' }
         - { name: Windows (mingw64+CMake),      os: windows-latest, shell: 'msys2 {0}', msystem: mingw64, msys-env: mingw-w64-x86_64, shared: 1, static: 0,
-            cmake: '-DSDLTTF_VENDORED=OFF -G "Ninja Multi-Config"' }
-        - { name: Linux,                        os: ubuntu-20.04,   shell: sh,   cmake: '-DSDLTTF_VENDORED=ON -GNinja', shared: 1, static: 0 }
-        - { name: 'Linux (static)',             os: ubuntu-20.04,   shell: sh,   cmake: '-GNinja -DBUILD_SHARED_LIBS=OFF', shared: 0, static: 1 }
-        - { name: Macos,                        os: macos-latest,   shell: sh,   cmake: '-DSDLTTF_VENDORED=ON -GNinja', shared: 1, static: 0 }
+            cmake: '-DSDLTTF_VENDORED=OFF -G "Ninja Multi-Config"', artifact: 'SDL3_ttf-mingw64' }
+        - { name: Linux,                        os: ubuntu-20.04,   shell: sh,   cmake: '-DSDLTTF_VENDORED=ON -GNinja', shared: 1, static: 0, artifact: 'SDL3_image-linux-x64' }
+        - { name: 'Linux (static)',             os: ubuntu-20.04,   shell: sh,   cmake: '-GNinja -DBUILD_SHARED_LIBS=OFF', shared: 0, static: 1, artifact: 'SDL3_Image-static-linux-x64' }
+        - { name: Macos,                        os: macos-latest,   shell: sh,   cmake: '-DSDLTTF_VENDORED=ON -GNinja', shared: 1, static: 0, artifact: 'SDL3_image-macos' }
 
     steps:
     - uses: ilammy/msvc-dev-cmd@v1
@@ -80,9 +80,9 @@ jobs:
       if: ${{ runner.os == 'Linux' }}
       run: ./build-scripts/test-versioning.sh
 
-    - name: Configure
+    - name: Configure (CMake)
       run: |
-        cmake -B build-cmake \
+        cmake -B build \
           -DBUILD_SHARED_LIBS=ON \
           -DSDLTTF_HARFBUZZ=ON \
           -DSDLTTF_SAMPLES=ON \
@@ -92,23 +92,29 @@ jobs:
           -DSDLTTF_INSTALL_MAN=ON \
           -DCMAKE_INSTALL_PREFIX=prefix_cmake \
           ${{ matrix.platform.cmake }}
-    - name: Build
+    - name: Build (CMake)
+      id: build
       run: |
-        cmake --build build-cmake --config Release --verbose
-    - name: Install
+        cmake --build build --config Release --verbose
+    - name: Install (CMake)
       run: |
         set -eu
         rm -rf prefix_cmake
-        cmake --install build-cmake/ --config Release --verbose
+        cmake --install build/ --config Release --verbose
         echo "SDL3_ttf_ROOT=$(pwd)/prefix_cmake" >> $GITHUB_ENV
         ( cd prefix_cmake; find . ) | LC_ALL=C sort -u
+    - name: Package (CPack)
+      id: package
+      if: ${{ always() && steps.build.outcome == 'success' }}
+      run: |
+        cmake --build build/ --target package
     - name: Test using showfont
       if: ${{ runner.os == 'Linux' }}
       run: |
           # Just check that it doesn't crash, we can't really test the results...
-          env -C build-cmake/ SDL_VIDEODRIVER=dummy ./showfont -dump /usr/share/fonts/truetype/dejavu/DejaVuSans.ttf
+          env -C build/ SDL_VIDEODRIVER=dummy ./showfont -dump /usr/share/fonts/truetype/dejavu/DejaVuSans.ttf
           # ... but we can at least assert that it outputs a .bmp
-          file build-cmake/glyph-100.bmp
+          file build/glyph-100.bmp
 
     - name: Verify CMake configuration files
       run: |
@@ -117,3 +123,9 @@ jobs:
             -DTEST_SHARED=${{ matrix.platform.shared }} \
             -DTEST_STATIC=${{ matrix.platform.static }}
           cmake --build cmake_config_build --verbose --config Release
+    - uses: actions/upload-artifact@v4
+      if: ${{ always() && steps.package.outcome == 'success' }}
+      with:
+        if-no-files-found: error
+        name: ${{ matrix.platform.artifact }}
+        path: build/dist/SDL3_ttf*
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6c40afa7..6f69bf10 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -17,9 +17,11 @@ project(SDL3_ttf
     VERSION "${MAJOR_VERSION}.${MINOR_VERSION}.${MICRO_VERSION}"
 )
 
-include("${SDL3_ttf_SOURCE_DIR}/cmake/GetGitRevisionDescription.cmake")
-include("${SDL3_ttf_SOURCE_DIR}/cmake/PrivateSdlFunctions.cmake")
-include("${SDL3_ttf_SOURCE_DIR}/cmake/sdlmanpages.cmake")
+include("${CMAKE_CURRENT_LIST_DIR}/cmake/GetGitRevisionDescription.cmake")
+include("${CMAKE_CURRENT_LIST_DIR}/cmake/PrivateSdlFunctions.cmake")
+include("${CMAKE_CURRENT_LIST_DIR}/cmake/sdlcpu.cmake")
+include("${CMAKE_CURRENT_LIST_DIR}/cmake/sdlplatform.cmake")
+include("${CMAKE_CURRENT_LIST_DIR}/cmake/sdlmanpages.cmake")
 sdl_calculate_derived_version_variables(${MAJOR_VERSION} ${MINOR_VERSION} ${MICRO_VERSION})
 
 message(STATUS "Configuring ${PROJECT_NAME} ${PROJECT_VERSION}")
@@ -76,6 +78,7 @@ option(CMAKE_POSITION_INDEPENDENT_CODE "Build static libraries with -fPIC" ${PLA
 cmake_dependent_option(BUILD_SHARED_LIBS "Build the library as a shared library" ON PLATFORM_SUPPORTS_SHARED OFF)
 
 cmake_dependent_option(SDLTTF_INSTALL "Enable SDL3_ttf install target" ${SDLTTF_ROOTPROJECT} "${sdl3ttf_install_enableable}" OFF)
+cmake_dependent_option(SDLTTF_INSTALL_CPACK "Create binary SDL3_ttf archive using CPack" ${SDLTTF_ROOTPROJECT} "SDLTTF_INSTALL" OFF)
 cmake_dependent_option(SDLTTF_INSTALL_MAN "Install man pages for SDL3_ttf" OFF "SDLTTF_INSTALL" OFF)
 cmake_dependent_option(SDLTTF_RELOCATABLE "Create relocatable SDL_ttf package" "${MSVC}" SDLTTF_INSTALL OFF)
 option(SDLTTF_VENDORED "Use vendored third-party libraries" ${vendored_default})
@@ -110,6 +113,9 @@ if(NOT TARGET SDL3::Headers OR NOT TARGET ${sdl3_target_name})
     find_package(SDL3 ${SDL_REQUIRED_VERSION} REQUIRED COMPONENTS ${sdl_required_components})
 endif()
 
+SDL_DetectTargetCPUArchitectures(SDL_CPU_NAMES)
+SDL_DetectCMakePlatform()
+
 # Enable large file support on 32-bit glibc, so that the vendored libraries
 # can access files with large inode numbers
 check_symbol_exists("__GLIBC__" "stdlib.h" LIBC_IS_GLIBC)
@@ -376,6 +382,20 @@ if(SDLTTF_INSTALL)
         COMPONENT library
     )
 
+    if(SDLTTF_INSTALL_CPACK)
+        if(MSVC)
+            set(CPACK_GENERATOR "ZIP")
+        else()
+            set(CPACK_GENERATOR "TGZ")
+        endif()
+        configure_file(cmake/CPackProjectConfig.cmake.in CPackProjectConfig.cmake @ONLY)
+        set(CPACK_PROJECT_CONFIG_FILE "${PROJECT_BINARY_DIR}/CPackProjectConfig.cmake")
+        # CPACK_SOURCE_PACKAGE_FILE_NAME must end with "-src" (so we can block creating a source archive)
+        set(CPACK_SOURCE_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}-src")
+        set(CPACK_PACKAGE_DIRECTORY "${CMAKE_BINARY_DIR}/dist")
+        include(CPack)
+    endif()
+
     if(SDLTTF_INSTALL_MAN)
         sdl_get_git_revision_hash(SDLTTF_REVISION)
         SDL_generate_manpages(
diff --git a/cmake/CPackProjectConfig.cmake.in b/cmake/CPackProjectConfig.cmake.in
new file mode 100644
index 00000000..4c7ce7d5
--- /dev/null
+++ b/cmake/CPackProjectConfig.cmake.in
@@ -0,0 +1,37 @@
+if(CPACK_PACKAGE_FILE_NAME MATCHES ".*-src$")
+    message(FATAL_ERROR "Creating source archives is not supported.")
+endif()
+
+set(PROJECT_NAME "@PROJECT_NAME@")
+set(PROJECT_VERSION "@PROJECT_VERSION@")
+set(PROJECT_SOURCE_DIR "@PROJECT_SOURCE_DIR@")
+set(SDL_CMAKE_PLATFORM "@SDL_CMAKE_PLATFORM@")
+set(SDL_CPU_NAMES "@SDL_CPU_NAMES@")
+list(SORT SDL_CPU_NAMES)
+
+string(TOLOWER "${SDL_CMAKE_PLATFORM}" SDL_CMAKE_PLATFORM)
+string(TOLOWER "${SDL_CPU_NAMES}" SDL_CPU_NAMES)
+if(lower_sdl_cmake_platform STREQUAL lower_sdl_cpu_names)
+    set(SDL_CPU_NAMES_WITH_DASHES)
+endif()
+
+string(REPLACE ";" "-" SDL_CPU_NAMES_WITH_DASHES "${SDL_CPU_NAMES}")
+if(SDL_CPU_NAMES_WITH_DASHES)
+    set(SDL_CPU_NAMES_WITH_DASHES "-${SDL_CPU_NAMES_WITH_DASHES}")
+endif()
+
+set(MSVC @MSVC@)
+set(MINGW @MINGW@)
+if(MSVC)
+    set(SDL_CMAKE_PLATFORM "${SDL_CMAKE_PLATFORM}-VC")
+elseif(MINGW)
+    set(SDL_CMAKE_PLATFORM "${SDL_CMAKE_PLATFORM}-mingw")
+endif()
+
+set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}-${SDL_CMAKE_PLATFORM}${SDL_CPU_NAMES_WITH_DASHES}")
+
+if(CPACK_GENERATOR STREQUAL "DragNDrop")
+    set(CPACK_DMG_VOLUME_NAME "@PROJECT_NAME@ @PROJECT_VERSION@")
+    # FIXME: use pre-built/create .DS_Store through AppleScript (CPACK_DMG_DS_STORE/CPACK_DMG_DS_STORE_SETUP_SCRIPT)
+    set(CPACK_DMG_DS_STORE "${PROJECT_SOURCE_DIR}/Xcode/SDL/pkg-support/resources/SDL_DS_Store")
+endif()
diff --git a/cmake/sdlplatform.cmake b/cmake/sdlplatform.cmake
new file mode 100644
index 00000000..ebb2077f
--- /dev/null
+++ b/cmake/sdlplatform.cmake
@@ -0,0 +1,106 @@
+macro(SDL_DetectCMakePlatform)
+  set(SDL_CMAKE_PLATFORM )
+  # Get the platform
+  if(WIN32)
+    set(SDL_CMAKE_PLATFORM Windows)
+  elseif(PSP)
+    set(SDL_CMAKE_PLATFORM psp)
+  elseif(APPLE)
+    if(CMAKE_SYSTEM_NAME MATCHES ".*Darwin.*")
+      set(SDL_CMAKE_PLATFORM Darwin)
+    elseif(CMAKE_SYSTEM_NAME MATCHES ".*MacOS.*")
+      set(SDL_CMAKE_PLATFORM MacosX)
+    elseif(CMAKE_SYSTEM_NAME MATCHES ".*tvOS.*")
+      set(SDL_CMAKE_PLATFORM tvOS)
+    elseif(CMAKE_SYSTEM_NAME MATCHES ".*iOS.*")
+      set(SDL_CMAKE_PLATFORM iOS)
+    endif()
+  elseif(CMAKE_SYSTEM_NAME MATCHES "Haiku.*")
+    set(SDL_CMAKE_PLATFORM Haiku)
+  elseif(NINTENDO_3DS)
+    set(SDL_CMAKE_PLATFORM n3ds)
+  elseif(PS2)
+    set(SDL_CMAKE_PLATFORM ps2)
+  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 MATCHES ".*GNU.*")
+    set(SDL_CMAKE_PLATFORM GNU)
+  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)
+  else()
+    set(SDL_CMAKE_PLATFORM} "unknown")
+  endif()
+endmacro()
+
+function(SDL_DetectCPUArchitecture)
+  set(sdl_cpu_names)
+  if(APPLE AND CMAKE_OSX_ARCHITECTURES)
+    foreach(osx_arch ${CMAKE_OSX_ARCHITECTURES})
+      if(osx_arch STREQUAL "x86_64")
+        list(APPEND sdl_cpu_names "x64")
+      elseif(osx_arch STREQUAL "arm64")
+        list(APPEND sdl_cpu_names "arm64")
+      endif()
+    endforeach()
+  endif()
+
+  set(sdl_known_archs x64 x86 arm64 arm32 emscripten powerpc64 powerpc32 loongarch64)
+  if(NOT sdl_cpu_names)
+    set(found FALSE)
+    foreach(sdl_known_arch ${sdl_known_archs})
+      if(NOT found)
+        string(TOUPPER "${sdl_known_arch}" sdl_known_arch_upper)
+        set(var_name "SDL_CPU_${sdl_known_arch_upper}")
+        check_cpu_architecture(${sdl_known_arch} ${var_name})
+        if(${var_name})
+          list(APPEND sdl_cpu_names ${sdl_known_arch})
+          set(found TRUE)
+        endif()
+      endif()
+    endforeach()
+  endif()
+
+  foreach(sdl_known_arch ${sdl_known_archs})
+    string(TOUPPER "${sdl_known_arch}" sdl_known_arch_upper)
+    set(var_name "SDL_CPU_${sdl_known_arch_upper}")
+    if(sdl_cpu_names MATCHES "(^|;)${sdl_known_arch}($|;)")  # FIXME: use if(IN_LIST)
+      set(${var_name} 1 PARENT_SCOPE)
+    else()
+      set(${var_name} 0 PARENT_SCOPE)
+    endif()
+  endforeach()
+  set(SDL_CPU_NAMES ${sdl_cpu_names} PARENT_SCOPE)
+endfunction()