SDL_image: Improve CMake build system

From 4ba0cf79115f351d51c7eac5a34b6bd9c1420d1b Mon Sep 17 00:00:00 2001
From: Anonymous Maarten <[EMAIL REDACTED]>
Date: Thu, 12 May 2022 04:06:06 +0200
Subject: [PATCH] Improve CMake build system

- Use 'SDL2IMAGE_' prefix for all options
- Create SDL2_image::SDL2_image when building a shared library,
  create SDL2_image::SDL2_image-static when building a static library
- Use same CMake layout as SDL_ttf/SDL_mixer
- Create libSDL2_imaged.so symbolic link (when building debug build type)
- Add PrivateSdlFunctions.cmake script for common functionality between SDL_XXX
- Add FindXXX.cmake scripts for dependencies that don't provide one
- Add CMakeLists.txt + cmake scripts to source distribution
- Bring jpeg/png saving support to cmake
- Update the git submodules
- Test CMake on mingw64 (using system dependencies there)
- Add MacOS CI
- Create symbolic link at build time + install it
- Add Macos versioning
- Add single SDL2IMAGE_VENDORED option to enable vendoring of
  dependencies
---
 .github/workflows/main.yml      |  51 +-
 CMakeLists.txt                  | 955 ++++++++++++++++----------------
 SDL2_imageConfig.cmake.in       | 121 ++--
 cmake/Findlibjxl.cmake          |  32 ++
 cmake/Findwebp.cmake            |  32 ++
 cmake/PrivateSdlFunctions.cmake | 232 ++++++++
 external/jpeg                   |   2 +-
 external/libjxl                 |   2 +-
 release_checklist.md            |   3 +
 test-versioning.sh              |   9 +
 test/CMakeLists.txt             |  21 +-
 11 files changed, 878 insertions(+), 582 deletions(-)
 create mode 100644 cmake/Findlibjxl.cmake
 create mode 100644 cmake/Findwebp.cmake
 create mode 100644 cmake/PrivateSdlFunctions.cmake

diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index c8039738..017c0547 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -15,10 +15,13 @@ jobs:
       fail-fast: false
       matrix:
         platform:
-        - { name: Windows (mingw32),      os: windows-latest, shell: 'msys2 {0}', msystem: mingw32, msys-env: mingw-w64-i686 }
-        - { name: Windows (mingw64),      os: windows-latest, shell: 'msys2 {0}', msystem: mingw64, msys-env: mingw-w64-x86_64 }
-        - { name: Linux (CMake),          os: ubuntu-20.04,   shell: sh,   cmake: '-GNinja' }
-        - { name: Linux (autotools),      os: ubuntu-20.04,   shell: sh }
+        - { name: Windows (mingw32+autotools),  os: windows-latest, shell: 'msys2 {0}', msystem: mingw32, msys-env: mingw-w64-i686 }
+        - { name: Windows (mingw64+CMake),      os: windows-latest, shell: 'msys2 {0}', msystem: mingw64, msys-env: mingw-w64-x86_64,
+            cmake: '-DSDL2IMAGE_BACKEND_STB=OFF -DSDL2IMAGE_BACKEND_WIC=OFF -DSDL2IMAGE_VENDORED=OFF -DSDL2IMAGE_AVIF=ON -G "Ninja Multi-Config"' }
+        - { name: Linux (autotools),            os: ubuntu-20.04,   shell: sh }
+        - { name: Linux (CMake),                os: ubuntu-20.04,   shell: sh,   cmake: '-DSDL2IMAGE_VENDORED=ON -GNinja' }
+        - { name: Macos (autotools),            os: macos-latest,   shell: sh }
+        - { name: Macos (CMake),                os: macos-latest,   shell: sh,   cmake: '-DSDL2IMAGE_VENDORED=ON -GNinja' }
 
     steps:
     - name: Set up MSYS2
@@ -31,14 +34,32 @@ jobs:
           ${{ matrix.platform.msys-env }}-autotools
           ${{ matrix.platform.msys-env }}-cmake
           ${{ matrix.platform.msys-env }}-gcc
+          ${{ matrix.platform.msys-env }}-libavif
           ${{ matrix.platform.msys-env }}-libjpeg-turbo
+          ${{ matrix.platform.msys-env }}-libjxl
           ${{ matrix.platform.msys-env }}-libpng
           ${{ matrix.platform.msys-env }}-libtiff
           ${{ matrix.platform.msys-env }}-libwebp
           ${{ matrix.platform.msys-env }}-ninja
           ${{ matrix.platform.msys-env }}-pkg-config
           ${{ matrix.platform.msys-env }}-zlib
-
+    - name: Setup Macos dependencies
+      if: runner.os == 'macOS'
+      run: |
+          brew install \
+            autoconf \
+            automake \
+            jpeg \
+            libavif \
+            libpng \
+            libtiff \
+            libtool \
+            ninja \
+            pkg-config \
+            sdl2 \
+            webp \
+            zlib \
+            ${NULL+}
     - name: Setup Linux dependencies
       if: runner.os == 'Linux'
       run: |
@@ -47,7 +68,6 @@ jobs:
           autoconf \
           automake \
           cmake \
-          gnome-desktop-testing \
           libjpeg-dev \
           libpng-dev \
           libsdl2-dev \
@@ -58,6 +78,7 @@ jobs:
           pkg-config \
           zlib1g-dev \
           ${NULL+}
+
     - uses: actions/checkout@v2
       with:
         submodules: recursive
@@ -70,23 +91,23 @@ jobs:
       if: "matrix.platform.cmake"
       run: |
         cmake -B build \
-          -DBUILD_SAMPLES=ON \
-          -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
-          -DCMAKE_VERBOSE_MAKEFILE=ON \
-          -DSUPPORT_JXL=OFF \
-          -DSUPPORT_TIF=ON \
-          -DSUPPORT_WEBP=ON \
+          -DBUILD_SHARED_LIBS=ON \
+          -DCMAKE_BUILD_TYPE=Release \
+          -DSDL2IMAGE_SAMPLES=ON \
+          -DSDL2IMAGE_JXL=ON \
+          -DSDL2IMAGE_TIF=ON \
+          -DSDL2IMAGE_WEBP=ON \
           ${{ matrix.platform.cmake }}
     - name: Build
       if: "matrix.platform.cmake"
-      run: cmake --build build/ --config Release
+      run: cmake --build build/ --config Release --verbose
     - name: Install
       if: "matrix.platform.shell == 'sh' && matrix.platform.cmake"
       run: |
         set -eu
         rm -fr DESTDIR-cmake
         DESTDIR=$(pwd)/DESTDIR-cmake cmake --install build/ --config Release
-        ( cd DESTDIR-cmake; find ) | LC_ALL=C sort -u
+        find DESTDIR-cmake | LC_ALL=C sort -u
 
     - name: Configure Autotools
       if: "! matrix.platform.cmake"
@@ -147,7 +168,7 @@ jobs:
         rm -fr DESTDIR-autotools
         mkdir DESTDIR-autotools
         make -j"${parallel}" -C build-autotools install DESTDIR="${curdir}/DESTDIR-autotools" V=1
-        ( cd DESTDIR-autotools; find ) | LC_ALL=C sort -u
+        find DESTDIR-autotools | LC_ALL=C sort -u
     - name: Distcheck with Autotools
       if: "runner.os == 'Linux' && ! matrix.platform.cmake"
       run: |
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 24978ecc..da06d955 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,315 +1,175 @@
 cmake_minimum_required(VERSION 3.14)
 
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
+
 # See docs/release_checklist.md
 set(MAJOR_VERSION 2)
 set(MINOR_VERSION 5)
 set(MICRO_VERSION 0)
-set(FULL_VERSION "${MAJOR_VERSION}.${MINOR_VERSION}.${MICRO_VERSION}")
 set(SDL_REQUIRED_VERSION 2.0.8)
 
+# For historical reasons this is 3.0.0 rather than the expected 1.0.0
+set(DYLIB_COMPATIBILITY_VERSION "3.0.0")
+
+include(PrivateSdlFunctions)
+sdl_calculate_derived_version_variables()
+
+if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
+    message(FATAL_ERROR "Prevented in-tree built. Please create a build directory outside of the SDL_image source code and call cmake from there")
+endif()
+
+project(SDL2_image
+    LANGUAGES C
+    VERSION "${FULL_VERSION}"
+)
+
+message(STATUS "Configuring ${PROJECT_NAME} ${PROJECT_VERSION}")
+
 # Set defaults preventing destination file conflicts
-set(SDL_CMAKE_DEBUG_POSTFIX "d"
+set(SDL2IMAGE_DEBUG_POSTFIX "d"
     CACHE STRING "Name suffix for debug builds")
+mark_as_advanced(SDL2IMAGE_DEBUG_POSTFIX)
 
-mark_as_advanced(CMAKE_IMPORT_LIBRARY_SUFFIX SDL_CMAKE_DEBUG_POSTFIX)
-
-# Calculate a libtool-like version number
-math(EXPR BINARY_AGE "${MINOR_VERSION} * 100 + ${MICRO_VERSION}")
-math(EXPR SDL2_IMAGE_DEVELOPMENT "${MINOR_VERSION} % 2")
-if (SDL2_IMAGE_DEVELOPMENT)
-    # Development branch, 2.5.1 -> libSDL2_image-2.0.so.0.501.0
-    set(INTERFACE_AGE 0)
+# Assume MSVC projects don't have a package manager and need vendored dependencies (by default).
+# Most other platforms have some kind of package manager.
+# FIXME: consider a package manager such as conan/vcpkg instead of vendoring
+if(MSVC)
+    set(vendored_default ON)
 else()
-    # Stable branch, 2.6.1 -> libSDL2_image-2.0.so.0.600.1
-    set(INTERFACE_AGE ${MICRO_VERSION})
-endif()
-
-# Increment this if there is an incompatible change - but if that happens,
-# we should rename the library from SDL2 to SDL3, at which point this would
-# reset to 0 anyway.
-set(LT_MAJOR "0")
-
-math(EXPR LT_AGE "${BINARY_AGE} - ${INTERFACE_AGE}")
-math(EXPR LT_CURRENT "${LT_MAJOR} + ${LT_AGE}")
-set(LT_REVISION "${INTERFACE_AGE}")
-# For historical reasons, the library name redundantly includes the major
-# version twice: libSDL2_image-2.0.so.0.
-# TODO: in SDL 3, set the OUTPUT_NAME to plain SDL3_image, which will simplify
-# it to libSDL3_image.so.0
-set(LT_RELEASE "2.0")
-set(LT_VERSION "${LT_MAJOR}.${LT_AGE}.${LT_REVISION}")
-
-# The following should match the versions in the Xcode project file.
-# Each version is 1 higher than you might expect, for compatibility
-# with libtool: macOS ABI versioning is 1-based, unlike other platforms
-# which are normally 0-based.
-math(EXPR DYLIB_CURRENT_VERSION_MAJOR "${LT_MAJOR} + ${LT_AGE} + 1")
-math(EXPR DYLIB_CURRENT_VERSION_MINOR "${LT_REVISION}")
-math(EXPR DYLIB_COMPAT_VERSION_MAJOR "${LT_MAJOR} + 1")
-set(DYLIB_CURRENT_VERSION "${DYLIB_CURRENT_VERSION_MAJOR}.${DYLIB_CURRENT_VERSION_MINOR}.0")
-# For historical reasons this is 3.0.0 rather than the expected 1.0.0
-set(DYLIB_COMPATIBILITY_VERSION "3.0.0")
+    set(vendored_default OFF)
+endif()
 
-project(SDL2_image LANGUAGES C
-    VERSION "${FULL_VERSION}")
+include(CMakeDependentOption)
+include(CMakePackageConfigHelpers)
+include(GNUInstallDirs)
 
-message(STATUS "Configuring ${PROJECT_NAME} ${PROJECT_VERSION}")
+option(CMAKE_POSITION_INDEPENDENT_CODE "Build static libraries with -fPIC" ON)
+option(BUILD_SHARED_LIBS "Build the library as a shared library" ON)
 
-if (NOT (TARGET SDL2::SDL2 OR TARGET SDL2::SDL2-static))
-    find_package(SDL2 REQUIRED)
+option(SDL2IMAGE_INSTALL "Enable SDL2_image install target" ON)
+option(SDL2IMAGE_VENDORED "Use vendored third-party libraries" ${vendored_default})
+
+option(SDL2IMAGE_SAMPLES "Build the SDL2_image sample program(s)" ON)
+cmake_dependent_option(SDL2IMAGE_SAMPLES_INSTALL "Install the SDL2_image sample program(s)" OFF "SDL2IMAGE_SAMPLES;SDL2IMAGE_INSTALL" OFF)
+
+option(SDL2IMAGE_TESTS "Build unit tests?" OFF)
+cmake_dependent_option(SDL2IMAGE_TESTS_INSTALL "Install unit tests?" OFF "SDL2IMAGE_TESTS;SDL2IMAGE_INSTALL" OFF)
+
+option(SDL2IMAGE_BACKEND_STB "Use stb_image for loading JPEG and PNG files" ON)
+cmake_dependent_option(SDL2IMAGE_BACKEND_WIC "Add WIC backend (Windows Imaging Component)" OFF WIN32 OFF)
+cmake_dependent_option(SDL2IMAGE_BACKEND_IMAGEIO "Use native Mac OS X frameworks for loading images" ON APPLE OFF)
+
+option(SDL2IMAGE_AVIF "Support loading AVIF images" OFF)
+option(SDL2IMAGE_BMP "Support loading BMP images" ON)
+option(SDL2IMAGE_GIF "Support loading GIF images" ON)
+option(SDL2IMAGE_JPG "Support loading JPEG images" ON)
+option(SDL2IMAGE_JXL "Support loading JXL images" OFF)
+option(SDL2IMAGE_LBM "Support loading LBM images" ON)
+option(SDL2IMAGE_PCX "Support loading PCX images" ON)
+option(SDL2IMAGE_PNG "Support loading PNG images" ON)
+option(SDL2IMAGE_PNM "Support loading PNM images" ON)
+option(SDL2IMAGE_QOI "Support loading QOI images" ON)
+option(SDL2IMAGE_SVG "Support loading SVG images" ON)
+option(SDL2IMAGE_TGA "Support loading TGA images" ON)
+option(SDL2IMAGE_TIF "Support loading TIFF images" OFF)
+option(SDL2IMAGE_WEBP "Support loading WEBP images" OFF)
+option(SDL2IMAGE_XCF "Support loading XCF images" ON)
+option(SDL2IMAGE_XPM "Support loading XPM images" ON)
+option(SDL2IMAGE_XV "Support loading XV images" ON)
+
+cmake_dependent_option(SDL2IMAGE_JPG_SAVE "Add JPEG save support" ON SDL2IMAGE_JPG OFF)
+cmake_dependent_option(SDL2IMAGE_PNG_SAVE "Add PNG save support" ON SDL2IMAGE_PNG OFF)
+
+if(SDL2IMAGE_VENDORED AND SDL2IMAGE_AVIF)
+    set(SDL2IMAGE_AVIF_VENDORED ON)
+else()
+    set(SDL2IMAGE_AVIF_VENDORED OFF)
 endif()
+cmake_dependent_option(SDL2IMAGE_AVIF_SHARED "Dynamically load AVIF support (requires shared libavif)"
+    ON SDL2IMAGE_AVIF OFF)
 
-function(read_absolute_symlink DEST PATH)
-    file(READ_SYMLINK "${PATH}" p)
-    if (NOT IS_ABSOLUTE p)
-        get_filename_component(pdir "${PATH}" DIRECTORY)
-        set(p "${pdir}/${p}")
-    endif()
-    set("${DEST}" "${p}" PARENT_SCOPE)
-endfunction()
-
-function(win32_implib_identify_dll DEST IMPLIB)
-    cmake_parse_arguments(ARGS "NOTFATAL" "" "" ${ARGN})
-    if (CMAKE_DLLTOOL)
-        execute_process(
-            COMMAND "${CMAKE_DLLTOOL}" --identify "${IMPLIB}"
-            RESULT_VARIABLE retcode
-            OUTPUT_VARIABLE stdout
-            ERROR_VARIABLE stderr)
-        if (NOT retcode EQUAL 0)
-            if (NOT ARGS_NOTFATAL)
-                message(FATAL_ERROR "${CMAKE_DLLTOOL} failed.")
-            else()
-                set("${DEST}" "${DEST}-NOTFOUND" PARENT_SCOPE)
-                return()
-            endif()
-        endif()
-        string(STRIP "${stdout}" result)
-        set(${DEST} "${result}" PARENT_SCOPE)
-    elseif (MSVC)
-        get_filename_component(CMAKE_C_COMPILER_DIRECTORY "${CMAKE_C_COMPILER}" DIRECTORY CACHE)
-        find_program(CMAKE_DUMPBIN NAMES dumpbin PATHS "${CMAKE_C_COMPILER_DIRECTORY}")
-        if (CMAKE_DUMPBIN)
-            execute_process(
-                COMMAND "${CMAKE_DUMPBIN}" "-headers" "${IMPLIB}"
-                RESULT_VARIABLE retcode
-                OUTPUT_VARIABLE stdout
-                ERROR_VARIABLE stderr)
-            if (NOT retcode EQUAL 0)
-                if (NOT ARGS_NOTFATAL)
-                    message(FATAL_ERROR "dumpbin failed.")
-                else()
-                    set(${DEST} "${DEST}-NOTFOUND" PARENT_SCOPE)
-                    return()
-                endif()
-            endif()
-            string(REGEX MATCH "DLL name[ ]+:[ ]+([^\n]+)\n" match "${stdout}")
-            if (NOT match)
-                if (NOT ARGS_NOTFATAL)
-                    message(FATAL_ERROR "dumpbin did not find any associated dll for ${IMPLIB}.")
-                else()
-                    set(${DEST} "${DEST}-NOTFOUND" PARENT_SCOPE)
-                    return()
-                endif()
-            endif()
-            set(result "${CMAKE_MATCH_1}")
-            set(${DEST} "${result}" PARENT_SCOPE)
-        else()
-            message(FATAL_ERROR "Cannot find dumpbin, please set CMAKE_DUMPBIN cmake variable")
-        endif()
-    else()
-        if (NOT ARGS_NOTFATAL)
-            message(FATAL_ERROR "Don't know how to identify dll from import library. Set CMAKE_DLLTOOL (for mingw) or CMAKE_DUMPBIN (for MSVC)")
-        else()
-            set(${DEST} "${DEST}-NOTFOUND")
-        endif()
-    endif()
-endfunction()
-
-function(target_get_dynamic_library DEST TARGET)
-    set(result)
-    get_target_property(alias "${TARGET}" ALIASED_TARGET)
-    while (alias)
-        set(TARGET "${alias}")
-        get_target_property(alias "${TARGET}" ALIASED_TARGET)
-    endwhile()
-    if (WIN32)
-        # Use the target dll of the import library
-        set(props_to_check IMPORTED_IMPLIB)
-        if (CMAKE_BUILD_TYPE)
-            list(APPEND props_to_check IMPORTED_IMPLIB_${CMAKE_BUILD_TYPE})
-        endif()
-        list(APPEND props_to_check IMPORTED_LOCATION)
-        if (CMAKE_BUILD_TYPE)
-            list(APPEND props_to_check IMPORTED_LOCATION_${CMAKE_BUILD_TYPE})
-        endif()
-        foreach (config_type ${CMAKE_CONFIGURATION_TYPES} RELEASE DEBUG RELWITHDEBINFO MINSIZEREL)
-            list(APPEND props_to_check IMPORTED_IMPLIB_${config_type})
-            list(APPEND props_to_check IMPORTED_LOCATION_${config_type})
-        endforeach()
-
-        foreach(prop_to_check ${props_to_check})
-            if (NOT result)
-                get_target_property(propvalue "${TARGET}" ${prop_to_check})
-                if (propvalue AND EXISTS "${propvalue}")
-                    win32_implib_identify_dll(result "${propvalue}" NOTFATAL)
-                endif()
-            endif()
-        endforeach()
-    else()
-        # 1. find the target library a file might be symbolic linking to
-        # 2. find all other files in the same folder that symolic link to it
-        # 3. sort all these files, and select the 2nd item
-        set(props_to_check IMPORTED_LOCATION)
-        if (CMAKE_BUILD_TYPE)
-            list(APPEND props_to_check IMPORTED_LOCATION_${CMAKE_BUILD_TYPE})
-        endif()
-        foreach (config_type ${CMAKE_CONFIGURATION_TYPES} RELEASE DEBUG RELWITHDEBINFO MINSIZEREL)
-            list(APPEND props_to_check IMPORTED_LOCATION_${config_type})
-        endforeach()
-        foreach(prop_to_check ${props_to_check})
-            if (NOT result)
-                get_target_property(propvalue "${TARGET}" ${prop_to_check})
-                if (EXISTS "${propvalue}")
-                    while (IS_SYMLINK "${propvalue}")
-                        read_absolute_symlink(propvalue "${propvalue}")
-                    endwhile()
-                    get_filename_component(libdir "${propvalue}" DIRECTORY)
-                    file(GLOB subfiles "${libdir}/*")
-                    set(similar_files "${propvalue}")
-                    foreach(subfile ${subfiles})
-                        if (IS_SYMLINK "${subfile}")
-                            read_absolute_symlink(subfile_target "${subfile}")
-                            if (subfile_target STREQUAL propvalue)
-                                list(APPEND similar_files "${subfile}")
-                            endif()
-                        endif()
-                    endforeach()
-                    list(SORT similar_files)
-                    list(LENGTH similar_files eq_length)
-                    if (eq_length GREATER 1)
-                        list(GET similar_files 1 item)
-                    else()
-                        list(GET similar_files 0 item)
-                    endif()
-                    get_filename_component(result "${item}" NAME)
-                endif()
-            endif()
-        endforeach()
-    endif()
-    if (NOT result)
-        set (result "$<TARGET_FILE_NAME:${TARGET}>")
-    endif()
-    set(${DEST} ${result} PARENT_SCOPE)
-endfunction()
-
-# Workaround for Ubuntu 20.04's SDL being older than
-# https://github.com/libsdl-org/SDL/issues/3531
-if (NOT (TARGET SDL2::SDL2 OR TARGET SDL2::SDL2-static))
-    find_library(SDL2_LIBRARY
-        NAMES SDL2
-        HINTS "${SDL2_EXEC_PREFIX}"
-        REQUIRED)
-    if (NOT SDL2_LIBRARY)
-        message(FATAL_ERROR "Could not find SDL2 library. Please define SDL2_EXEC_PREFIX and/or SLD2_LIBRARY")
-    endif()
-    add_library(SDL2::SDL2 SHARED IMPORTED)
-    set_target_properties(SDL2::SDL2 PROPERTIES
-        INTERFACE_INCLUDE_DIRECTORIES "${SDL2_INCLUDE_DIRS}"
-        IMPORTED_LINK_INTERFACE_LANGUAGES "C"
-        IMPORTED_LOCATION "${SDL2_LIBRARY}")
+if(SDL2IMAGE_VENDORED AND SDL2IMAGE_JPG AND NOT (SDL2IMAGE_BACKEND_WIC OR SDL2IMAGE_BACKEND_STB OR SDL2IMAGE_BACKEND_IMAGEIO))
+    set(SDL2IMAGE_JPG_VENDORED ON)
+else()
+    set(SDL2IMAGE_JPG_VENDORED OFF)
 endif()
+cmake_dependent_option(SDL2IMAGE_JPG_SHARED "Dynamically load JPG support (requires shared libjpeg)"
+    ON "SDL2IMAGE_JPG;NOT SDL2IMAGE_BACKEND_WIC;NOT SDL2IMAGE_BACKEND_STB;NOT SDL2IMAGE_BACKEND_IMAGEIO" OFF)
 
-# Set PROJECT_VERSION of subprojects to "" if it's project call does not set VERSION
-if (POLICY CMP0048)
-    cmake_policy(SET CMP0048 NEW)
+if(SDL2IMAGE_VENDORED AND SDL2IMAGE_JXL)
+    set(SDL2IMAGE_JXL_VENDORED ON)
+else()
+    set(SDL2IMAGE_JXL_VENDORED OFF)
 endif()
+cmake_dependent_option(SDL2IMAGE_JXL_SHARED "Dynamically load JXL support (requires shared libjxl)"
+    ON SDL2IMAGE_JXL OFF)
 
-# Alloc cmake_dependent_option to use "Full Condition Syntax"
-if (POLICY CMP0127)
-    cmake_policy(SET CMP0127 NEW)
+if(SDL2IMAGE_VENDORED AND SDL2IMAGE_PNG AND NOT (SDL2IMAGE_BACKEND_WIC OR SDL2IMAGE_BACKEND_STB OR SDL2IMAGE_BACKEND_IMAGEIO))
+    set(SDL2IMAGE_PNG_VENDORED ON)
+else()
+    set(SDL2IMAGE_PNG_VENDORED OFF)
 endif()
+cmake_dependent_option(SDL2IMAGE_PNG_SHARED "Dynamically load PNG support (requires shared libpng)"
+    ON "SDL2IMAGE_PNG;NOT SDL2IMAGE_BACKEND_WIC;NOT SDL2IMAGE_BACKEND_STB;NOT SDL2IMAGE_BACKEND_IMAGEIO" OFF)
 
-# OpenGL is required by dependencies of (dependencies of) some vendored libraries
-if (NOT DEFINED OpenGL_GL_PREFERENCE)
-    set(OpenGL_GL_PREFERENCE GLVND)
+if(SDL2IMAGE_VENDORED AND SDL2IMAGE_TIF)
+    set(SDL2IMAGE_TIF_VENDORED ON)
+else()
+    set(SDL2IMAGE_TIF_VENDORED OFF)
 endif()
+cmake_dependent_option(SDL2IMAGE_TIF_SHARED "Dynamically load TIFF support (requires shared libtiff)"
+    ON SDL2IMAGE_TIF OFF)
 
-include(CMakeDependentOption)
-include(CMakePackageConfigHelpers)
-include(GNUInstallDirs)
+if(SDL2IMAGE_VENDORED AND SDL2IMAGE_WEBP)
+    set(SDL2IMAGE_WEBP_VENDORED ON)
+else()
+    set(SDL2IMAGE_WEBP_VENDORED OFF)
+endif()
+cmake_dependent_option(SDL2IMAGE_WEBP_SHARED "Dynamically load WEBP support (requires shared libwebp)"
+    ON SDL2IMAGE_WEBP OFF)
 
-option(BUILD_TESTS "Build unit tests?" OFF)
-cmake_dependent_option(INSTALL_TESTS "Install unit tests?" OFF BUILD_TESTS OFF)
-option(VENDORED_DEFAULT "Default value for *_VENDORED options. Can be overridden for each library. Is only used in the first configure run." ON)
-option(SDL2_IMAGE_DISABLE_INSTALL "Disable installing SDL2_image" OFF)
-
-option(BACKEND_STB "Use stb_image for loading JPEG and PNG files" ON)
-cmake_dependent_option(BACKEND_WIC "Add WIC backend (Windows Imaging Component)" OFF "WIN32" OFF)
-cmake_dependent_option(BACKEND_IMAGEIO "Use native Mac OS X frameworks for loading images" ON APPLE OFF)
-
-option(SUPPORT_AVIF "Support loading AVIF images" OFF)
-option(SUPPORT_BMP "Support loading BMP images" ON)
-option(SUPPORT_GIF "Support loading GIF images" ON)
-option(SUPPORT_JPG "Support loading JPEG images" ON)
-option(SUPPORT_JXL "Support loading JXL images" OFF)
-option(SUPPORT_LBM "Support loading LBM images" ON)
-option(SUPPORT_PCX "Support loading PCX images" ON)
-option(SUPPORT_PNG "Support loading PNG images" ON)
-option(SUPPORT_PNM "Support loading PNM images" ON)
-option(SUPPORT_QOI "Support loading QOI images" ON)
-option(SUPPORT_SVG "Support loading SVG images" ON)
-option(SUPPORT_TGA "Support loading TGA images" ON)
-option(SUPPORT_TIF "Support loading TIFF images" OFF)
-option(SUPPORT_WEBP "Support loading WEBP images" OFF)
-option(SUPPORT_XCF "Support loading XCF images" ON)
-option(SUPPORT_XPM "Support loading XPM images" ON)
-option(SUPPORT_XV "Support loading XV images" ON)
-
-option(BUILD_SAMPLES "Build the SDL2_image sample program(s)" ON)
-option(BUILD_SHARED_LIBS "Build the library as a shared library" ON)
-# FIXME: use vendored libavif when available
-set(SUPPORT_AVIF_VENDORED OFF)
-#cmake_dependent_option(SUPPORT_AVIF_VENDORED "Use vendored libavif" ${VENDORED_DEFAULT} SUPPORT_AVIF OFF)
-cmake_dependent_option(SUPPORT_AVIF_SHARED "Dynamically load AVIF support (requires shared libavif)" ON
-    "SUPPORT_AVIF;(SUPPORT_AVIF_VENDORED AND BUILD_SHARED_LIBS) OR NOT SUPPORT_AVIF_VENDORED" OFF)
-
-cmake_dependent_option(SUPPORT_JPG_VENDORED "Use vendored libjpeg" ${VENDORED_DEFAULT} "SUPPORT_JPG;NOT BACKEND_WIC;NOT BACKEND_STB;NOT BACKEND_IMAGEIO;" OFF)
-cmake_dependent_option(SUPPORT_JPG_SHARED "Dynamically load JPG support (requires shared libjpeg)" ON
-    "SUPPORT_JPG;NOT BACKEND_WIC;NOT BACKEND_STB;NOT BACKEND_IMAGEIO;(SUPPORT_JPG_VENDORED AND BUILD_SHARED_LIBS) OR NOT SUPPORT_JPG_VENDORED" OFF)
-
-cmake_dependent_option(SUPPORT_JXL_VENDORED "Use vendored libjxl" ${VENDORED_DEFAULT} SUPPORT_JXL OFF)
-cmake_dependent_option(SUPPORT_JXL_SHARED "Dynamically load JXL support (requires shared libjxl)" ON
-    "SUPPORT_JXL;(SUPPORT_JXL_VENDORED AND BUILD_SHARED_LIBS) OR NOT SUPPORT_JXL_VENDORED" OFF)
-
-cmake_dependent_option(SUPPORT_PNG_VENDORED "Use vendored PNG" ${VENDORED_DEFAULT} "SUPPORT_PNG;NOT BACKEND_WIC;NOT BACKEND_STB;NOT BACKEND_IMAGEIO" OFF)
-cmake_dependent_option(SUPPORT_PNG_SHARED "Dynamically load PNG support (requires shared libpng)" ON
-    "SUPPORT_PNG;NOT BACKEND_WIC;NOT BACKEND_STB;NOT BACKEND_IMAGEIO;(SUPPORT_PNG_VENDORED AND BUILD_SHARED_LIBS) OR NOT SUPPORT_PNG_VENDORED" OFF)
-cmake_dependent_option(SUPPORT_ZLIB_VENDORED "Use vendored ZLIB (required by PNG)" ${VENDORED_DEFAULT} SUPPORT_PNG_VENDORED OFF)
-
-cmake_dependent_option(SUPPORT_TIF_VENDORED "Use vendored libtiff" ${VENDORED_DEFAULT} SUPPORT_TIF OFF)
-cmake_dependent_option(SUPPORT_TIF_SHARED "Dynamically load TIFF support (requires shared libtiff)" ON
-    "SUPPORT_TIF;(SUPPORT_TIF_VENDORED AND BUILD_SHARED_LIBS) OR NOT SUPPORT_TIF_VENDORED" OFF)
-
-cmake_dependent_option(SUPPORT_WEBP_VENDORED "Use vendored WEBP" ${VENDORED_DEFAULT} SUPPORT_WEBP OFF)
-cmake_dependent_option(SUPPORT_WEBP_SHARED "Dynamically load WEBP support (requires shared libwebp)" ON
-    "SUPPORT_WEBP;(SUPPORT_WEBP_VENDORED AND BUILD_SHARED_LIBS) OR NOT SUPPORT_WEBP_VENDORED" OFF)
-
-# FIXME: ????
-if (NOT BUILD_SHARED_LIBS)
-    set(CMAKE_POSITION_INDEPENDENT_CODE ON)
+if(SDL2IMAGE_VENDORED AND SDL2IMAGE_PNG_VENDORED)
+    set(SDL2IMAGE_ZLIB_VENDORED ON)
+else()
+    set(SDL2IMAGE_ZLIB_VENDORED OFF)
+endif()
+if(SDL2IMAGE_PNG_SHARED)
+    set(SDL2IMAGE_ZLIB_SHARED ON)
+else()
+    set(SDL2IMAGE_ZLIB_SHARED OFF)
 endif()
 
-if(BUILD_SHARED_LIBS)
+# Save BUILD_SHARED_LIBS variable
+set(SDL2IMAGE_BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS})
+
+if(SDL2IMAGE_BUILD_SHARED_LIBS)
     set(sdl2_image_export_name SDL2_image)
     set(sdl2_image_install_name_infix shared)
+    set(sdl2_target_name SDL2::SDL2)
 else()
     set(sdl2_image_export_name SDL2_image-static)
     set(sdl2_image_install_name_infix static)
+    set(sdl2_target_name SDL2::SDL2-static)
+endif()
+
+sdl_find_sdl2(${sdl2_target_name} ${SDL_REQUIRED_VERSION})
+
+# Set PROJECT_VERSION of subprojects to "" if it's project call does not set VERSION
+cmake_policy(SET CMP0048 NEW)
+
+# Allow cmake_dependent_option to use "Full Condition Syntax"
+if(POLICY CMP0127)
+    cmake_policy(SET CMP0127 NEW)
+endif()
+
+# OpenGL is required by dependencies of (dependencies of) some vendored libraries
+if(NOT DEFINED OpenGL_GL_PREFERENCE)
+    set(OpenGL_GL_PREFERENCE GLVND)
 endif()
 
 add_library(SDL2_image
     IMG.c
+    IMG_WIC.c
     IMG_avif.c
     IMG_bmp.c
     IMG_gif.c
@@ -324,74 +184,157 @@ add_library(SDL2_image
     IMG_svg.c
     IMG_tga.c
     IMG_tif.c
-    IMG_WIC.c
     IMG_webp.c
     IMG_xcf.c
     IMG_xpm.c
     IMG_xv.c
     IMG_xxx.c
-    )
+)
 add_library(SDL2_image::${sdl2_image_export_name} ALIAS SDL2_image)
-
+target_include_directories(SDL2_image PUBLIC
+    "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
+    "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/SDL2>"
+)
 target_compile_definitions(SDL2_image PRIVATE
     SDL_BUILD_MAJOR_VERSION=${MAJOR_VERSION}
     SDL_BUILD_MINOR_VERSION=${MINOR_VERSION}
     SDL_BUILD_MICRO_VERSION=${MICRO_VERSION}
+)
+target_link_libraries(SDL2_image PRIVATE ${sdl2_target_name})
+if(WIN32 AND SDL2IMAGE_BUILD_SHARED_LIBS)
+    target_sources(SDL2_image PRIVATE
+        version.rc
+)
+endif()
+set_target_properties(SDL2_image PROPERTIES
+    DEFINE_SYMBOL DLL_EXPORT
+    PUBLIC_HEADER SDL_image.h
+    EXPORT_NAME ${sdl2_image_export_name}
+    C_VISIBILITY_PRESET "hidden"
+)
+if(NOT ANDROID)
+    set_target_properties(SDL2_image PROPERTIES
+        DEBUG_POSTFIX "${SDL2IMAGE_DEBUG_POSTFIX}"
     )
+    if(APPLE)
+        # the SOVERSION property corresponds to the compatibility version and VERSION corresponds to the current version
+        # https://cmake.org/cmake/help/latest/prop_tgt/SOVERSION.html#mach-o-versions
+        set_target_properties(SDL2_image PROPERTIES
+            SOVERSION "${DYLIB_COMPATIBILITY_VERSION}"
+            VERSION "${DYLIB_CURRENT_VERSION}"
+        )
+    else()
+        set_target_properties(SDL2_image PROPERTIES
+            SOVERSION "${LT_MAJOR}"
+            VERSION "${LT_VERSION}"
+        )
+    endif()
+endif()
+if(SDL2IMAGE_BUILD_SHARED_LIBS AND (APPLE OR (UNIX AND NOT ANDROID)))
+    add_custom_command(TARGET SDL2_image POST_BUILD
+        COMMAND "${CMAKE_COMMAND}" -E create_symlink "$<TARGET_SONAME_FILE_NAME:SDL2_image>" "libSDL2_image$<$<CONFIG:Debug>:${SDL2IMAGE_DEBUG_POSTFIX}>$<TARGET_FILE_SUFFIX:SDL2_image>"
+        # BYPRODUCTS "libSDL2_image$<$<CONFIG:Debug>:${SDL2IMAGE_DEBUG_POSTFIX}>$<TARGET_FILE_SUFFIX:SDL2_image>" # Needs CMake 3.20
+        WORKING_DIRECTORY "${PROJECT_BINARY_DIR}"
+    )
+endif()
+if(SDL2IMAGE_BUILD_SHARED_LIBS)
+    if(WIN32 OR OS2)
+        set_target_properties(SDL2_image PROPERTIES
+            PREFIX ""
+        )
+    endif()
+    if(OS2)
+        # OS/2 doesn't support a DLL name longer than 8 characters.
+        set_target_properties(SDL2_image PROPERTIES
+            OUTPUT_NAME "SDL2img"
+        )
+    elseif(UNIX AND NOT ANDROID)
+        set_target_properties(SDL2_image PROPERTIES
+            OUTPUT_NAME "SDL2_image-${LT_RELEASE}"
+        )
+    endif()
+endif()
+
+# Use `Compatible Interface Properties` to ensure all SDL2 libraries are built with the same "sharedness".
+set_property(TARGET SDL2_image PROPERTY INTERFACE_SDL2_SHARED ${SDL2IMAGE_BUILD_SHARED_LIBS})
+set_property(TARGET SDL2_image APPEND PROPERTY COMPATIBLE_INTERFACE_BOOL SDL2_SHARED)
+
+set(INSTALL_EXTRA_TARGETS)
+set(PC_LIBS)
+set(PC_REQUIRES)
 
-if (BACKEND_STB)
+if(SDL2IMAGE_BACKEND_STB)
     target_compile_definitions(SDL2_image PRIVATE USE_STBIMAGE)
 endif()
 
-if (APPLE)
-    if (BACKEND_IMAGEIO)
+if(SDL2IMAGE_BUILD_SHARED_LIBS)
+    # Make sure static library dependencies are built with -fPIC when building a shared SDL2_image
+    set(CMAKE_POSITION_INDEPENDENT_CODE ON)
+endif()
+
+if(APPLE)
+    if(SDL2IMAGE_BACKEND_IMAGEIO)
         target_link_options(SDL2_image PRIVATE -Wl,-framework,ApplicationServices)
         target_link_libraries(SDL2_image PRIVATE objc)
         target_sources(SDL2_image PRIVATE
             IMG_ImageIO.m
-            )
+        )
     else()
         target_compile_definitions(SDL2_image PRIVATE SDL_IMAGE_USE_COMMON_BACKEND)
     endif()
 endif()
 
-if (BACKEND_WIC)
+if(SDL2IMAGE_BACKEND_WIC)
     target_compile_definitions(SDL2_image PRIVATE SDL_IMAGE_USE_WIC_BACKEND)
 endif()
 
-if (WIN32 AND BUILD_SHARED_LIBS)
-    target_sources(SDL2_image PRIVATE
-        version.rc
-        )
+if(SDL2IMAGE_ZLIB_VENDORED)
+    message(STATUS "${PROJECT_NAME}: Using vendored zlib")
+    sdl_check_project_in_subfolder(external/zlib zlib)
+    add_subdirectory(external/zlib EXCLUDE_FROM_ALL)
+    # PNG_BUILD_ZLIB variable is used by vendored libpng
+    set(PNG_BUILD_ZLIB ON CACHE BOOL "libpng option to tell it should use 'our' vendored ZLIB library" FORCE)
+    # ZLIB_INCLUDE_DIR variable is used by vendored libpng
+    set(ZLIB_INCLUDE_DIR "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/external/zlib;${CMAKE_CURRENT_BINARY_DIR}/external/zlib>" CACHE STRING "path of zlib, passed to libpng" FORCE)
+    # ZLIB_LIBRARY variable is used by vendored libpng
+    if(SDL2IMAGE_ZLIB_SHARED)
+        set(ZLIB_LIBRARY zlib)
+    else()
+        set(ZLIB_LIBRARY zlibstatic)
+    endif()
+    list(APPEND INSTALL_EXTRA_TARGETS ${ZLIB_LIBRARY})
+else()
+    message(STATUS "${PROJECT_NAME}: Using system zlib")
+    find_package(ZLIB REQUIRED)
 endif()
 
-set(INSTALL_EXTRA_TARGETS)
-set(PC_REQUIRES)
-set(PC_LIBS)
-
-if (SUPPORT_AVIF)
+if(SDL2IMAGE_AVIF)
     target_compile_definitions(SDL2_image PRIVATE LOAD_AVIF)
-    if (SUPPORT_AVIF_VENDORED)
-        

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