SDL_ttf: Major CMake rework

From 11846cb683fa44276673b3e5fbe0ae60e2f766f4 Mon Sep 17 00:00:00 2001
From: Anonymous Maarten <[EMAIL REDACTED]>
Date: Thu, 12 May 2022 05:08:16 +0200
Subject: [PATCH] Major CMake rework

- Use 'SDL2TTF_' prefix for all options
- Create SDL2_ttf::SDL2_ttf when building a shared library,
  create SDL2_ttf::SDL2_ttf-static when building a static library
- Use same CMake layout as SDL_image/SDL_mixer
- Create libSDL2_ttfd.so symbolic link (when building debug build type)
- Add PrivateSdlFunctions.cmake script for common functionality between SDL_image/SDL_mixer
- Add FindXXX.cmake scripts for dependencies that don't provide cmake configuration files
- Add CMakeLists.txt + cmake scripts to source distribution (EXTRA_DIST of autotools)
- Test CMake on mingw64 (using system dependencies there)
- Add MacOS CI
- Create symbolic link at build time + install it
- Only install SDL2_ttf.pc when building a shared SDL2_ttf (to avoid overwriting when later installing a static SDL2_tff)
- Allow parallel installation with cmake support of a static and shared SDL2_ttf
- Macos versioning
---
 .github/fetch_sdl_vc.ps1                      |  40 ++
 .github/workflows/main.yml                    | 106 +++--
 CMakeLists.txt                                | 428 ++++++++++--------
 Makefile.am                                   |  10 +
 Makefile.in                                   |  45 +-
 SDL2_ttfConfig.cmake.in                       |  55 ++-
 .../cmake/sdl2_ttf-config-version.cmake       |  54 +++
 .../pkg-support/cmake/sdl2_ttf-config.cmake   |  51 +++
 .../CMake/sdl2_ttf-config-version.cmake       |  48 ++
 .../resources/CMake/sdl2_ttf-config.cmake     |  39 ++
 aclocal.m4                                    | 110 ++++-
 cmake/CommonFindSDL2.cmake                    |  22 +
 cmake/FindPrivateSDL2.cmake                   |  47 ++
 cmake/FindSDL2main.cmake                      |  24 +
 cmake/Findharfbuzz.cmake                      |  36 ++
 cmake/PrivateSdlFunctions.cmake               | 243 ++++++++++
 cmake/test/CMakeLists.txt                     |  49 ++
 cmake/test/main.c                             |  18 +
 configure                                     |  58 ++-
 configure.ac                                  |  14 +
 .../cmake/sdl2_ttf-config-version.cmake       |  19 +
 mingw/pkg-support/cmake/sdl2_ttf-config.cmake |  19 +
 release_checklist.md                          |  27 +-
 sdl2_ttf-config-version.cmake.in              |  12 +
 sdl2_ttf-config.cmake.in                      |  94 ++++
 test-versioning.sh                            |  17 +
 26 files changed, 1413 insertions(+), 272 deletions(-)
 create mode 100644 .github/fetch_sdl_vc.ps1
 create mode 100644 VisualC/pkg-support/cmake/sdl2_ttf-config-version.cmake
 create mode 100644 VisualC/pkg-support/cmake/sdl2_ttf-config.cmake
 create mode 100644 Xcode/pkg-support/resources/CMake/sdl2_ttf-config-version.cmake
 create mode 100644 Xcode/pkg-support/resources/CMake/sdl2_ttf-config.cmake
 create mode 100644 cmake/CommonFindSDL2.cmake
 create mode 100644 cmake/FindPrivateSDL2.cmake
 create mode 100644 cmake/FindSDL2main.cmake
 create mode 100644 cmake/Findharfbuzz.cmake
 create mode 100644 cmake/PrivateSdlFunctions.cmake
 create mode 100644 cmake/test/CMakeLists.txt
 create mode 100644 cmake/test/main.c
 create mode 100644 mingw/pkg-support/cmake/sdl2_ttf-config-version.cmake
 create mode 100644 mingw/pkg-support/cmake/sdl2_ttf-config.cmake
 create mode 100644 sdl2_ttf-config-version.cmake.in
 create mode 100644 sdl2_ttf-config.cmake.in

diff --git a/.github/fetch_sdl_vc.ps1 b/.github/fetch_sdl_vc.ps1
new file mode 100644
index 00000000..84eec242
--- /dev/null
+++ b/.github/fetch_sdl_vc.ps1
@@ -0,0 +1,40 @@
+$ErrorActionPreference = "Stop"
+
+$project_root = "$psScriptRoot\.."
+Write-Output "project_root: $project_root"
+
+$sdl2_version = "2.0.10"
+$sdl2_zip = "SDL2-devel-$($sdl2_version)-VC.zip"
+
+$sdl2_url = "https://github.com/libsdl-org/SDL/releases/download/release-$($sdl2_version)/SDL2-devel-$($sdl2_version)-VC.zip"
+$sdl2_dlpath = "$($Env:TEMP)\$sdl2_zip"
+
+$sdl2_bindir = "$($project_root)"
+$sdl2_extractdir = "$($sdl2_bindir)\SDL2-$($sdl2_version)"
+$sdl2_root_name = "SDL2-devel-VC"
+
+echo "sdl2_bindir:     $sdl2_bindir"
+echo "sdl2_extractdir: $sdl2_extractdir"
+echo "sdl2_root_name:  $sdl2_root_name"
+
+echo "Cleaning previous artifacts"
+if (Test-Path $sdl2_extractdir) {
+    Remove-Item $sdl2_extractdir -Recurse -Force
+}
+if (Test-Path "$($sdl2_bindir)/$sdl2_root_name") {
+    Remove-Item "$($sdl2_bindir)/$sdl2_root_name" -Recurse -Force
+}
+if (Test-Path $sdl2_dlpath) {
+    Remove-Item $sdl2_dlpath -Force
+}
+
+Write-Output "Downloading $sdl2_url"
+Invoke-WebRequest -Uri $sdl2_url -OutFile $sdl2_dlpath
+
+Write-Output "Extracting archive"
+Expand-Archive $sdl2_dlpath -DestinationPath $sdl2_bindir
+
+Write-Output "Setting up SDL2 folder"
+Rename-Item $sdl2_extractdir $sdl2_root_name
+
+Write-Output "Done"
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 70503e0c..168fb355 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -15,10 +15,15 @@ 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 (MSVC+CMake),         os: windows-latest, shell: sh,   cmake: '-DSDL2TTF_VENDORED=ON -GNinja', msvc: 1, shared: 1, static: 0 }
+        - { name: Windows (mingw32+autotools),  os: windows-latest, shell: 'msys2 {0}', msystem: mingw32, msys-env: mingw-w64-i686, shared: 1, static: 1 }
+        - { name: Windows (mingw64+CMake),      os: windows-latest, shell: 'msys2 {0}', msystem: mingw64, msys-env: mingw-w64-x86_64, shared: 1, static: 0,
+            cmake: '-DSDL2TTF_VENDORED=OFF -G "Ninja Multi-Config"' }
+        - { name: Linux (autotools),            os: ubuntu-20.04,   shell: sh,   shared: 1, static: 1 }
+        - { name: Linux (CMake),                os: ubuntu-20.04,   shell: sh,   cmake: '-DSDL2TTF_VENDORED=ON -GNinja', shared: 1, static: 0 }
+        - { name: 'Linux (CMake, static)',      os: ubuntu-20.04,   shell: sh,   cmake: '-GNinja -DBUILD_SHARED_LIBS=OFF', shared: 0, static: 1 }
+        - { name: Macos (autotools),            os: macos-latest,   shell: sh,   shared: 1, static: 1}
+        - { name: Macos (CMake),                os: macos-latest,   shell: sh,   cmake: '-DSDL2TTF_VENDORED=ON -GNinja', shared: 1, static: 0 }
 
     steps:
     - name: Set up MSYS2
@@ -36,7 +41,19 @@ jobs:
           ${{ 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 \
+            freetype \
+            harfbuzz \
+            libtool \
+            ninja \
+            pkg-config \
+            sdl2 \
+            ${NULL+}
     - name: Setup Linux dependencies
       if: runner.os == 'Linux'
       run: |
@@ -57,66 +74,103 @@ jobs:
     - uses: actions/checkout@v2
       with:
         submodules: recursive
+
+    - name: Setup MSVC dependencies
+      if: "matrix.platform.msvc"
+      shell: pwsh
+      run: |
+        .github/fetch_sdl_vc.ps1
+        echo "SDL2_DIR=$Env:GITHUB_WORKSPACE/SDL2-devel-VC" >> $Env:GITHUB_ENV
+    - name: Setup Ninja for MSVC
+      if: "matrix.platform.msvc"
+      uses: ashutoshvarma/setup-ninja@master
+      with:
+        version: 1.10.2
+    - uses: ilammy/msvc-dev-cmd@v1
+      if: "matrix.platform.msvc"
+      with:
+        arch: x64
+
     - name: Check that versioning is consistent
       # We only need to run this once: arbitrarily use the Linux/CMake build
       if: "runner.os == 'Linux' && matrix.platform.cmake"
       run: ./test-versioning.sh
 
-    - name: Configure CMake
+    - name: Configure (CMake)
       if: "matrix.platform.cmake"
       run: |
-        cmake -B build \
-          -DTTF_WITH_HARFBUZZ=ON \
-          -DBUILD_SAMPLES=ON \
+        cmake -B build-cmake \
           -DBUILD_SHARED_LIBS=ON \
+          -DSDL2TTF_HARFBUZZ=ON \
+          -DSDL2TTF_SAMPLES=ON \
           -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
           -DCMAKE_BUILD_TYPE=Release \
+          -DCMAKE_INSTALL_PREFIX=prefix_cmake \
           ${{ matrix.platform.cmake }}
-    - name: Build
+    - name: Build (CMake)
+      if: "matrix.platform.cmake"
+      run: |
+        cmake --build build-cmake --config Release --verbose
+    - name: Install (CMake)
       if: "matrix.platform.cmake"
-      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 --verbose
-        ( cd DESTDIR-cmake; find ) | LC_ALL=C sort -u
+        rm -rf prefix_cmake
+        cmake --install build-cmake/ --config Release --verbose
+        echo "SDL2_ttf_DIR=$(pwd)/prefix_cmake" >> $GITHUB_ENV
+        ( cd prefix_cmake; find . ) | LC_ALL=C sort -u
+    - name: Test using showfont (CMake)
+      if: "runner.os == 'Linux' && matrix.platform.cmake"
+      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
+          # ... but we can at least assert that it outputs a .bmp
+          file build-cmake/glyph-100.bmp
 
-    - name: Configure Autotools
+    - name: Configure (Autotools)
       if: "! matrix.platform.cmake"
       run: |
         set -eu
         rm -fr build-autotools
         mkdir build-autotools
         ./autogen.sh
-        ( cd build-autotools && ../configure )
-    - name: Build with Autotools
+        set -- --prefix=$(pwd)/prefix_autotools
+        ( cd build-autotools && ../configure "$@" )
+    - name: Build (Autotools)
       if: "! matrix.platform.cmake"
       run: |
         set -eu
         parallel="$(getconf _NPROCESSORS_ONLN)"
         make -j"${parallel}" -C build-autotools V=1
-    - name: Test using showfont
+    - name: Test (showfont)
       if: "runner.os == 'Linux' && ! matrix.platform.cmake"
       run: |
         # Just check that it doesn't crash, we can't really test the results...
         env -C build-autotools 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-autotools/glyph-100.bmp
-    - name: Install with Autotools
+    - name: Install (Autotools)
       if: "! matrix.platform.cmake"
       run: |
         set -eu
         curdir="$(pwd)"
         parallel="$(getconf _NPROCESSORS_ONLN)"
-        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
-    - name: Distcheck with Autotools
+        rm -rf prefix_autotools
+        make -j"${parallel}" -C build-autotools install V=1
+        echo "SDL2_ttf_DIR=$(pwd)/prefix_autotools" >> $GITHUB_ENV
+        ( cd prefix_autotools; find . ) | LC_ALL=C sort -u
+    - name: Distcheck (Autotools)
       if: "runner.os == 'Linux' && ! matrix.platform.cmake"
       run: |
         set -eu
         parallel="$(getconf _NPROCESSORS_ONLN)"
         make -j"${parallel}" -C build-autotools distcheck V=1
+
+    - name: Verify CMake configuration files
+      run: |
+          cmake -S cmake/test -B cmake_config_build \
+            -DCMAKE_BUILD_TYPE=Release \
+            -DCMAKE_PREFIX_PATH="${{ env.SDL2_ttf_DIR }};${{ env.SDL2_DIR }}" \
+            -DTEST_SHARED=${{ matrix.platform.shared }} \
+            -DTEST_STATIC=${{ matrix.platform.static }}
+          cmake --build cmake_config_build --verbose --config Release
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 05d9f48e..a9e44b13 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,82 +1,42 @@
 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 19)
 set(MICRO_VERSION 1)
-set(SDL_TTF_VERSION "${MAJOR_VERSION}.${MINOR_VERSION}.${MICRO_VERSION}")
 set(SDL_REQUIRED_VERSION 2.0.10)
 
-# Set defaults preventing destination file conflicts
-set(SDL_CMAKE_DEBUG_POSTFIX "d"
-    CACHE STRING "Name suffix for debug builds")
-
-# Calculate a libtool-like version number
-math(EXPR BINARY_AGE "${MINOR_VERSION} * 100 + ${MICRO_VERSION}")
-if(MINOR_VERSION MATCHES "[02468]$")
-    # Stable branch, 2.6.1 -> libSDL2_ttf-2.0.so.0.600.1
-    set(INTERFACE_AGE ${MICRO_VERSION})
-else()
-    # Development branch, 2.5.1 -> libSDL2_ttf-2.0.so.0.501.0
-    set(INTERFACE_AGE 0)
-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_ttf-2.0.so.0.
-# TODO: in SDL 3, set the OUTPUT_NAME to plain SDL3_ttf, which will simplify
-# it to libSDL3_ttf.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 15.0.0 rather than the expected 1.0.0
 set(DYLIB_COMPATIBILITY_VERSION "15.0.0")
 
-if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR})
+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_ttf source code and call cmake from there")
 endif()
 
-project(SDL2_ttf LANGUAGES C
-    VERSION "${SDL_TTF_VERSION}")
+project(SDL2_ttf
+    LANGUAGES C
+    VERSION "${FULL_VERSION}"
+)
 
 message(STATUS "Configuring ${PROJECT_NAME} ${PROJECT_VERSION}")
 
-if (NOT (TARGET SDL2::SDL2 OR TARGET SDL2::SDL2-static))
-    find_package(SDL2 REQUIRED)
-endif()
+# Set defaults preventing destination file conflicts
+set(SDL2TTF_DEBUG_POSTFIX "d"
+    CACHE STRING "Name suffix for debug builds")
+mark_as_advanced(SDL2TTF_DEBUG_POSTFIX)
 
-# 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}"
-    )
+# 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 TRUE)
+else()
+    set(vendored_default FALSE)
 endif()
 
 include(CMakeDependentOption)
@@ -84,32 +44,40 @@ include(CMakePackageConfigHelpers)
 include(GNUInstallDirs)
 include(CheckSymbolExists)
 
-option(VENDORED_DEFAULT "Default value for *_VENDORED options. Can be overridden for each library. Is only used in the first configure run." ON)
+option(CMAKE_POSITION_INDEPENDENT_CODE "Build static libraries with -fPIC" ON)
+option(BUILD_SHARED_LIBS "Build the library as a shared library" ON)
 
-# For style consistency, create a TTF_WITH_FREETYPE CMake variable. This variable is NOT configurable.
-set(TTF_WITH_FREETYPE ON)
-cmake_dependent_option(TTF_WITH_FREETYPE_VENDORED "Use vendored freetype library" ${VENDORED_DEFAULT} TTF_WITH_FREETYPE OFF)
+option(SDL2TTF_SAMPLES "Build the SDL2_ttf sample program(s)" ON)
+option(SDL2TTF_INSTALL "Enable SDL2_ttf install target" ON)
+option(SDL2TTF_VENDORED "Use vendored third-party libraries" ${vendored_default})
 
-option(TTF_WITH_HARFBUZZ "Use harfbuzz to improve text shaping" OFF)
-cmake_dependent_option(TTF_WITH_HARFBUZZ_VENDORED "Use vendored harfbuzz library" ${VENDORED_DEFAULT} TTF_WITH_HARFBUZZ OFF)
+# For style consistency, create a SDL2TTF_FREETYPE CMake variable. This variable is NOT configurable.
+set(SDL2TTF_FREETYPE ON)
+set(SDL2TTF_FREETYPE_VENDORED "${SDL2TTF_VENDORED}")
 
-option(BUILD_SAMPLES "Build the SDL2_ttf sample program(s)" ON)
-option(BUILD_SHARED_LIBS "Build the library as a shared library" ON)
-option(TTF_DISABLE_INSTALL "Disable installing SDL2_ttf" OFF)
+option(SDL2TTF_HARFBUZZ "Use harfbuzz to improve text shaping" OFF)
+set(SDL2TTF_HARFBUZZ_VENDORED "${SDL2TTF_VENDORED}")
+
+# Save BUILD_SHARED_LIBS variable
+set(SDL2TTF_BUILD_SHARED_LIBS "${BUILD_SHARED_LIBS}")
 
-if(BUILD_SHARED_LIBS)
+if(SDL2TTF_BUILD_SHARED_LIBS)
     set(sdl2_ttf_export_name SDL2_ttf)
     set(sdl2_ttf_install_name_infix shared)
+    set(sdl2_target_name SDL2::SDL2)
 else()
     set(sdl2_ttf_export_name SDL2_ttf-static)
     set(sdl2_ttf_install_name_infix static)
+    set(sdl2_target_name SDL2::SDL2-static)
 endif()
 
+sdl_find_sdl2(${sdl2_target_name} ${SDL_REQUIRED_VERSION})
+
 # 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)
 if (LIBC_IS_GLIBC AND CMAKE_SIZEOF_VOID_P EQUAL 4)
-    add_compile_definitions(PRIVATE _FILE_OFFSET_BITS=64)
+    add_compile_definitions(_FILE_OFFSET_BITS=64)
 endif()
 
 add_library(SDL2_ttf
@@ -117,70 +85,150 @@ add_library(SDL2_ttf
     SDL_ttf.h
 )
 add_library(SDL2_ttf::${sdl2_ttf_export_name} ALIAS SDL2_ttf)
-
+target_include_directories(SDL2_ttf PUBLIC
+    "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
+    "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/SDL2>"
+)
 target_compile_definitions(SDL2_ttf PRIVATE
+    BUILD_SDL
     SDL_BUILD_MAJOR_VERSION=${MAJOR_VERSION}
     SDL_BUILD_MINOR_VERSION=${MINOR_VERSION}
     SDL_BUILD_MICRO_VERSION=${MICRO_VERSION}
 )
+target_link_libraries(SDL2_ttf PRIVATE $<BUILD_INTERFACE:${sdl2_target_name}>)
+if(WIN32 AND SDL2TTF_BUILD_SHARED_LIBS)
+    target_sources(SDL2_ttf PRIVATE
+        version.rc
+    )
+endif()
+set_target_properties(SDL2_ttf PROPERTIES
+    DEFINE_SYMBOL DLL_EXPORT
+    PUBLIC_HEADER SDL_ttf.h
+    EXPORT_NAME ${sdl2_ttf_export_name}
+    C_VISIBILITY_PRESET "hidden"
+)
+if(NOT ANDROID)
+    set_target_properties(SDL2_ttf PROPERTIES
+        DEBUG_POSTFIX "${SDL2TTF_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_ttf PROPERTIES
+            SOVERSION "${DYLIB_COMPATIBILITY_VERSION}"
+            VERSION "${DYLIB_CURRENT_VERSION}"
+        )
+    else()
+        set_target_properties(SDL2_ttf PROPERTIES
+            SOVERSION "${LT_MAJOR}"
+            VERSION "${LT_VERSION}"
+        )
+    endif()
+endif()
+if(SDL2TTF_BUILD_SHARED_LIBS AND (APPLE OR (UNIX AND NOT ANDROID)))
+    add_custom_command(TARGET SDL2_ttf POST_BUILD
+        COMMAND "${CMAKE_COMMAND}" -E create_symlink "$<TARGET_SONAME_FILE_NAME:SDL2_ttf>" "libSDL2_ttf$<$<CONFIG:Debug>:${SDL2TTF_DEBUG_POSTFIX}>$<TARGET_FILE_SUFFIX:SDL2_ttf>"
+        # BYPRODUCTS "libSDL2_ttf$<$<CONFIG:Debug>:${SDL2TTF_DEBUG_POSTFIX}>$<TARGET_FILE_SUFFIX:SDL2_ttf>" # Needs CMake 3.20
+        WORKING_DIRECTORY "${PROJECT_BINARY_DIR}"
+    )
+endif()
+if(SDL2TTF_BUILD_SHARED_LIBS)
+    if(WIN32 OR OS2)
+        set_target_properties(SDL2_ttf PROPERTIES
+            PREFIX ""
+        )
+    endif()
+    if(OS2)
+        # OS/2 doesn't support a DLL name longer than 8 characters.
+        set_target_properties(SDL2_ttf PROPERTIES
+            OUTPUT_NAME "SDL2ttf"
+        )
+    elseif(UNIX AND NOT ANDROID)
+        set_target_properties(SDL2_ttf PROPERTIES
+            OUTPUT_NAME "SDL2_ttf-${LT_RELEASE}"
+        )
+    endif()
+endif()
 
-if (BUILD_SHARED_LIBS)
-    target_link_libraries(SDL2_ttf PRIVATE SDL2::SDL2)
-else()
-    target_link_libraries(SDL2_ttf PRIVATE SDL2::SDL2-static)
+if(SDL2TTF_BUILD_SHARED_LIBS)
+    # Use `Compatible Interface Properties` to ensure a shared SDL2_ttf is linked to a shared SDL2 library
+    set_property(TARGET SDL2_ttf PROPERTY INTERFACE_SDL2_SHARED ${SDL2TTF_BUILD_SHARED_LIBS})
+    set_property(TARGET SDL2_ttf APPEND PROPERTY COMPATIBLE_INTERFACE_BOOL SDL2_SHARED)
 endif()
 
 set(INSTALL_EXTRA_TARGETS)
 set(PC_LIBS)
 set(PC_REQUIRES)
 
-# Save BUILD_SHARED_LIBS variable
-set(SDL2TTF_BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS})
-
-# Build freetype and harfbuzz as a static library (avoid cyclic dependency between shared libraries)
+# Build freetype and harfbuzz as a static library
 set(BUILD_SHARED_LIBS OFF)
 
-if (TTF_WITH_HARFBUZZ)
-    target_compile_definitions(SDL2_ttf PRIVATE TTF_USE_HARFBUZZ=1)
-    if (TTF_WITH_HARFBUZZ_VENDORED)
+if(SDL2TTF_HARFBUZZ)
+    if(SDL2TTF_HARFBUZZ_VENDORED)
+        message(STATUS "${PROJECT_NAME}: Using vendored harfbuzz library")
+        # HB_BUILD_UTILS variable is used by harfbuzz
         set(HB_BUILD_UTILS OFF CACHE BOOL "harfbuzz build utils" FORCE)
+        # SKIP_INSTALL_LIBRARIES variable is used by harfbuzz
         set(SKIP_INSTALL_LIBRARIES ON CACHE BOOL "harfbuzz install option" FORCE)
-        set(HB_HAVE_FREETYPE ${TTF_WITH_FREETYPE} CACHE BOOL "harfbuzz freetype helpers" FORCE)
+        # HB_HAVE_FREETYPE variable is used by harfbuzz
+        set(HB_HAVE_FREETYPE ${SDL2TTF_FREETYPE} CACHE BOOL "harfbuzz freetype helpers" FORCE)
+        if(NOT EXISTS "${PROJECT_SOURCE_DIR}/external/harfbuzz/CMakeLists.txt")
+            message(FATAL_ERROR "No harfbuzz sources found. Install a harfbuzz development package or run the download script in the external folder.")
+        endif()
         add_subdirectory(external/harfbuzz EXCLUDE_FROM_ALL)
-        list(APPEND INSTALL_EXTRA_TARGETS harfbuzz)
-        if (NOT TARGET harfbuzz::harfbuzz)
+        # harfbuzz is a c++ project, enable c++ here to ensure linking to the c++ standard library
+        enable_language(CXX)
+        if(NOT SDL2TTF_BUILD_SHARED_LIBS)
+            list(APPEND INSTALL_EXTRA_TARGETS harfbuzz)
+            list(APPEND PC_LIBS -l$<TARGET_FILE_BASE_NAME:harfbuzz>)
+        endif()
+        if(NOT TARGET harfbuzz::harfbuzz)
             add_library(harfbuzz::harfbuzz ALIAS harfbuzz)
         endif()
-        list(APPEND PC_LIBS -l$<TARGET_FILE_BASE_NAME:harfbuzz>)
     else()
+        message(STATUS "${PROJECT_NAME}: Using system harfbuzz library")
         find_package(harfbuzz REQUIRED)
         list(APPEND PC_REQUIRES harfbuzz)
     endif()
+    target_compile_definitions(SDL2_ttf PRIVATE TTF_USE_HARFBUZZ=1)
     target_link_libraries(SDL2_ttf PRIVATE harfbuzz::harfbuzz)
 endif()
 
-if (TTF_WITH_FREETYPE)
-    if (TTF_WITH_FREETYPE_VENDORED)
+if(SDL2TTF_FREETYPE)
+    if(SDL2TTF_FREETYPE_VENDORED)
+        message(STATUS "${PROJECT_NAME}: Using vendored freetype library")
+        # FT_DISABLE_ZLIB variable is used by freetype
         set(FT_DISABLE_ZLIB ON CACHE BOOL "freetype zlib option")
+        # FT_DISABLE_BZIP2 variable is used by freetype
         set(FT_DISABLE_BZIP2 ON CACHE BOOL "freetype bzip2 option")
+        # FT_DISABLE_PNG variable is used by freetype
         set(FT_DISABLE_PNG ON CACHE BOOL "freetype png option")
+        # FT_DISABLE_BROTLI variable is used by freetype
         set(FT_DISABLE_BROTLI ON CACHE BOOL "freetype option")
-        if (TTF_WITH_HARFBUZZ)
+        if(SDL2TTF_HARFBUZZ)
+            # FT_DISABLE_HARFBUZZ variable is used by freetype
             set(FT_DISABLE_HARFBUZZ OFF CACHE BOOL "freetype harfbuzz option" FORCE)
+            # FT_REQUIRE_HARFBUZZ variable is used by freetype
             set(FT_REQUIRE_HARFBUZZ ON CACHE BOOL "freetype harfbuzz option" FORCE)
-            #set(HarfBuzz_INCLUDE_DIR "$<TARGET_PROPERTY:harfbuzz::harfbuzz,INCLUDE_DIRECTORIES>" CACHE STRING "freetype harfbuzz include path" FORCE)
-            #set(HarfBuzz_LIBRARY harfbuzz::harfbuzz CACHE STRING "freetype harfbuzz library path" FORCE)
         else()
+            # FT_DISABLE_HARFBUZZ variable is used by freetype
             set(FT_DISABLE_HARFBUZZ ON CACHE BOOL "freetype harfbuzz option" FORCE)
+            # FT_REQUIRE_HARFBUZZ variable is used by freetype
             set(FT_REQUIRE_HARFBUZZ OFF CACHE BOOL "freetype harfbuzz option" FORCE)
         endif()
+        if(NOT EXISTS "${PROJECT_SOURCE_DIR}/external/freetype/CMakeLists.txt")
+            message(FATAL_ERROR "No freetype sources found. Install a freetype development package or run the download script in the external folder.")
+        endif()
         add_subdirectory(external/freetype EXCLUDE_FROM_ALL)
-        list(APPEND INSTALL_EXTRA_TARGETS freetype)
-        if (NOT TARGET Freetype::Freetype)
+        if(NOT TARGET Freetype::Freetype)
             add_library(Freetype::Freetype ALIAS freetype)
         endif()
-        list(APPEND PC_LIBS -l$<TARGET_FILE_BASE_NAME:freetype>)
+        if(NOT SDL2TTF_BUILD_SHARED_LIBS)
+            list(APPEND INSTALL_EXTRA_TARGETS freetype)
+            list(APPEND PC_LIBS -l$<TARGET_FILE_BASE_NAME:freetype>)
+        endif()
     else()
+        message(STATUS "${PROJECT_NAME}: Using system freetype library")
         find_package(Freetype REQUIRED)
         list(APPEND PC_REQUIRES freetype2)
     endif()
@@ -227,124 +275,132 @@ if (BUILD_SHARED_LIBS)
     endif()
 endif()
 
-if (WIN32 AND BUILD_SHARED_LIBS)
-    target_sources(SDL2_ttf PRIVATE version.rc)
-endif()
-
-target_include_directories(SDL2_ttf
-    PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
-    PUBLIC $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/SDL2>
-    )
+# Restore BUILD_SHARED_LIBS variable
+set(BUILD_SHARED_LIBS "${SDL2TTF_BUILD_SHARED_LIBS}")
 
-if (NOT TTF_DISABLE_INSTALL)
+if(SDL2TTF_INSTALL)
     install(
         TARGETS SDL2_ttf
         EXPORT SDL2_ttfTargets
-        PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/SDL2"
-        ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
-        LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
-        RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
+        ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT devel
+        LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT library
+        RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT library
+        PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/SDL2" COMPONENT devel
     )
 
-    if (INSTALL_EXTRA_TARGETS)
-        install(TARGETS ${INSTALL_EXTRA_TARGETS} EXPORT SDL2_ttfTargets
-            ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
-            LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
-            RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
-            PUBLIC_HEADER DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/external_include"
+    if(INSTALL_EXTRA_TARGETS)
+        install(TARGETS ${INSTALL_EXTRA_TARGETS}
+            EXPORT SDL2_ttfTargets
+            ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT devel
+            LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT library
+            RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT library
+            PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" COMPONENT devel
         )
     endif()
 
-    ##### export files #####
-    if (APPLE)
-        set(PKG_PREFIX "SDL2_ttf.framework/Resources")
-    elseif (WIN32)
+    if(WIN32 AND NOT MINGW)
         set(PKG_PREFIX "cmake")
-    else ()
+    else()
         set(PKG_PREFIX "${CMAKE_INSTALL_LIBDIR}/cmake/SDL2_ttf")
-    endif ()
+    endif()
 
     configure_package_config_file(SDL2_ttfConfig.cmake.in SDL2_ttfConfig.cmake
         INSTALL_DESTINATION "${PKG_PREFIX}"
     )
     write_basic_package_version_file("${PROJECT_BINARY_DIR}/SDL2_ttfConfigVersion.cmake"
-        VERSION ${SDL_TTF_VERSION}
+        VERSION ${FULL_VERSION}
         COMPATIBILITY AnyNewerVersion
     )
-
+    install(
+        FILES
+            "${CMAKE_CURRENT_BINARY_DIR}/SDL2_ttfConfig.cmake"
+            "${CMAKE_CURRENT_BINARY_DIR}/SDL2_ttfConfigVersion.cmake"
+        DESTINATION ${PKG_PREFIX}
+        COMPONENT devel
+    )
     install(EXPORT SDL2_ttfTargets
         FILE SDL2_ttf-${sdl2_ttf_install_name_infix}-targets.cmake
         NAMESPACE SDL2_ttf::
         DESTINATION "${PKG_PREFIX}"
+        COMPONENT devel
     )
 
-    set(VERSION ${SDL_TTF_VERSION})
-    set(SDL_VERSION ${SDL_REQUIRED_VERSION})
-    set(prefix "${CMAKE_INSTALL_PREFIX}")
-    set(exec_prefix "\${prefix}")
-    set(libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}")
-    set(bindir "\${exec_prefix}/bin")
-    set(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
-    string(JOIN " " PC_REQUIRES ${PC_REQUIRES})
-    string(JOIN " " PC_LIBS ${PC_LIBS})
-    configure_file(SDL2_ttf.pc.in ${CMAKE_CURRENT_BINARY_DIR}/SDL2_ttf.pc.intermediate @ONLY)
-    file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/SDL2_ttf.pc" INPUT "${CMAKE_CURRENT_BINARY_DIR}/SDL2_ttf.pc.intermediate")
-
-    if (CMAKE_SYSTEM_NAME MATCHES FreeBSD)
-        # FreeBSD uses ${PREFIX}/libdata/pkgconfig
-        install(FILES ${CMAKE_CURRENT_BINARY_DIR}/SDL2_ttf.pc DESTINATION "libdata/pkgconfig")
-    else ()
-        install(FILES ${CMAKE_CURRENT_BINARY_DIR}/SDL2_ttf.pc
-            DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
-    endif ()
-    install(FILES "LICENSE.txt" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/licenses/${PROJECT_NAME}")
-    if (NOT (WIN32 OR CYGWIN OR MINGW))
-        if(BUILD_SHARED_LIBS)
-            if(NOT ANDROID)
-                install(CODE "
-                    execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink
-                        \"$<TARGET_SONAME_FILE_NAME:SDL2_ttf>\" \"libSDL2_ttf$<$<CONFIG:Debug>:${SDL_CMAKE_DEBUG_POSTFIX}>$<TARGET_FILE_SUFFIX:SDL2_ttf>\"
-                        WORKING_DIRECTORY \"${PROJECT_BINARY_DIR}\")")
-                install(FILES "${PROJECT_BINARY_DIR}/libSDL2_ttf$<$<CONFIG:Debug>:${SDL_CMAKE_DEBUG_POSTFIX}>$<TARGET_FILE_SUFFIX:SDL2_ttf>" DESTINATION "${CMAKE_INSTALL_LIBDIR}")
-            endif()
+    if(SDL2TTF_BUILD_SHARED_LIBS)
+        # Only create a .pc file for a shared SDL2_ttf
+        set(pr

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