SDL_ttf: cmake: add/fix Find modules for plutosvg and harfbuzz

From ffc443932d4ef4d7f0e960a9bcd4dd95ff64b0c2 Mon Sep 17 00:00:00 2001
From: Anonymous Maarten <[EMAIL REDACTED]>
Date: Wed, 29 Jan 2025 01:41:02 +0100
Subject: [PATCH] cmake: add/fix Find modules for plutosvg and harfbuzz

---
 CMakeLists.txt                | 16 +++++++++++--
 cmake/Findharfbuzz.cmake      | 40 +++++++++++++++++++++++++++----
 cmake/Findplutosvg.cmake      | 45 +++++++++++++++++++++++++++++++++++
 cmake/PkgConfigHelper.cmake   | 34 ++++++++++++++++++++++++++
 cmake/SDL3_ttfConfig.cmake.in | 24 ++++++++++++-------
 5 files changed, 144 insertions(+), 15 deletions(-)
 create mode 100644 cmake/Findplutosvg.cmake
 create mode 100644 cmake/PkgConfigHelper.cmake

diff --git a/CMakeLists.txt b/CMakeLists.txt
index a7e6173c..ca3d346d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -18,6 +18,7 @@ project(SDL3_ttf
 )
 
 include("${CMAKE_CURRENT_LIST_DIR}/cmake/GetGitRevisionDescription.cmake")
+include("${CMAKE_CURRENT_LIST_DIR}/cmake/PkgConfigHelper.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")
@@ -85,7 +86,7 @@ option(SDLTTF_VENDORED "Use vendored third-party libraries" ${vendored_default})
 option(SDLTTF_WERROR "Treat warnings as errors" OFF)
 
 option(SDLTTF_STRICT "Fail when a dependency could not be found" OFF)
-set(find_package_strict_arg "QUIET")
+set(find_package_strict_arg )
 set(fatal_error "STATUS")
 if(SDLTTF_STRICT)
     set(find_package_strict_arg "REQUIRED")
@@ -99,6 +100,7 @@ cmake_dependent_option(SDLTTF_SAMPLES_INSTALL "Install the SDL3_ttf sample progr
 set(SDLTTF_FREETYPE ON)
 set(SDLTTF_FREETYPE_VENDORED "${SDLTTF_VENDORED}")
 
+set(HARFBUZZ_REQUIRED_VERSION "2.3.1")
 option(SDLTTF_HARFBUZZ "Use harfbuzz to improve text shaping" ON)
 set(SDLTTF_HARFBUZZ_VENDORED "${SDLTTF_VENDORED}")
 
@@ -265,7 +267,7 @@ if(SDLTTF_HARFBUZZ)
             endif()
         endif()
     else()
-        find_package(harfbuzz ${find_package_strict_arg})
+        find_package(harfbuzz "${HARFBUZZ_REQUIRED_VERSION}" ${find_package_strict_arg})
         if(harfbuzz_FOUND)
             message(STATUS "${PROJECT_NAME}: Using system harfbuzz library")
             list(APPEND PC_REQUIRES harfbuzz)
@@ -424,6 +426,16 @@ if(SDLTTF_INSTALL)
         DESTINATION ${SDLTTF_INSTALL_CMAKEDIR}
         COMPONENT devel
     )
+    if(NOT SDLTTF_VENDORED)
+      install(
+          FILES
+              cmake/PkgConfigHelper.cmake
+              cmake/Findharfbuzz.cmake
+              cmake/Findplutosvg.cmake
+          DESTINATION "${SDLTTF_INSTALL_CMAKEDIR}"
+          COMPONENT devel
+      )
+    endif()
     install(EXPORT SDL3_ttfTargets
         FILE ${sdl3_ttf_target_name}-targets.cmake
         NAMESPACE SDL3_ttf::
diff --git a/cmake/Findharfbuzz.cmake b/cmake/Findharfbuzz.cmake
index 53407e28..da289ae8 100644
--- a/cmake/Findharfbuzz.cmake
+++ b/cmake/Findharfbuzz.cmake
@@ -3,23 +3,52 @@
 
 include(FindPackageHandleStandardArgs)
 
+find_package(PkgConfig QUIET)
+pkg_check_modules(PC_HARFBUZZ QUIET harfbuzz)
+
 find_library(harfbuzz_LIBRARY
     NAMES harfbuzz
+    HINTS ${PC_HARFBUZZ_LIBDIR}
 )
 
 find_path(harfbuzz_INCLUDE_PATH
     NAMES hb.h
     PATH_SUFFIXES harfbuzz
+    HINTS ${PC_HARFBUZZ_INCLUDEDIR}
 )
 
-set(harfbuzz_COMPILE_FLAGS "" CACHE STRING "Extra compile flags of harfbuzz")
+if(PC_HARFBUZZ_FOUND)
+    get_flags_from_pkg_config("${harfbuzz_LIBRARY}" "PC_HARFBUZZ" "_harfbuzz")
+endif()
+
+set(harfbuzz_COMPILE_OPTIONS "${_harfbuzz_compile_options}" CACHE STRING "Extra compile flags of harfbuzz")
 
-set(harfbuzz_LINK_LIBRARIES "" CACHE STRING "Extra link libraries of harfbuzz")
+set(harfbuzz_LINK_LIBRARIES "${_harfbuzz_link_libraries}" CACHE STRING "Extra link libraries of harfbuzz")
 
-set(harfbuzz_LINK_FLAGS "" CACHE STRING "Extra link flags of harfbuzz")
+set(harfbuzz_LINK_OPTIONS "${_harfbuzz_link_options}" CACHE STRING "Extra link flags of harfbuzz")
+
+set(harfbuzz_LINK_DIRECTORIES "${_harfbuzz_link_directories}" CACHE PATH "Extra link directories of harfbuzz")
+
+set(harfbuzz_VERSION "harfbuzz_VERSION-NOTFOUND")
+foreach(_hb_incpath IN LISTS harfbuzz_INCLUDE_PATH)
+    if(IS_DIRECTORY "${_hb_incpath}" AND EXISTS "${_hb_incpath}/hb-version.h")
+        file(READ "${_hb_incpath}/hb-version.h" _hb_version_text)
+        string(REGEX MATCH "#define[ \t]+HB_VERSION_MAJOR[ \t]+([0-9]+)" _hb_major_re "${_hb_version_text}")
+        set(_hb_major "${CMAKE_MATCH_1}")
+        string(REGEX MATCH "#define[ \t]+HB_VERSION_MINOR[ \t]+([0-9]+)" _hb_minor_re "${_hb_version_text}")
+        set(_hb_minor "${CMAKE_MATCH_1}")
+        string(REGEX MATCH "#define[ \t]+HB_VERSION_MICRO[ \t]+([0-9]+)" _hb_micro_re "${_hb_version_text}")
+        set(_hb_micro "${CMAKE_MATCH_1}")
+        if(_hb_major_re AND _hb_minor_re AND _hb_micro_re)
+            set(harfbuzz_VERSION "${_hb_major}.${_hb_minor}.${_hb_micro}")
+            break()
+        endif()
+    endif()
+endforeach()
 
 find_package_handle_standard_args(harfbuzz
     REQUIRED_VARS harfbuzz_LIBRARY harfbuzz_INCLUDE_PATH
+    VERSION_VAR harfbuzz_VERSION
 )
 
 if (harfbuzz_FOUND)
@@ -28,9 +57,10 @@ if (harfbuzz_FOUND)
         set_target_properties(harfbuzz::harfbuzz PROPERTIES
             IMPORTED_LOCATION "${harfbuzz_LIBRARY}"
             INTERFACE_INCLUDE_DIRECTORIES "${harfbuzz_INCLUDE_PATH}"
-            COMPILE_FLAGS "${harfbuzz_COMPILE_FLAGS}"
+            INTERFACE_COMPILE_OPTIONS "${harfbuzz_COMPILE_OPTIONS}"
             INTERFACE_LINK_LIBRARIES "${harfbuzz_LINK_LIBRARIES}"
-            INTERFACE_LINK_FLAGS "${harfbuzz_LINK_FLAGS}"
+            INTERFACE_LINK_OPTIONS "${harfbuzz_LINK_OPTIONS}"
+            INTERFACE_LINK_DIRECTORIES "${harfbuzz_LINK_DIRECTORIES}"
         )
     endif()
 endif()
diff --git a/cmake/Findplutosvg.cmake b/cmake/Findplutosvg.cmake
new file mode 100644
index 00000000..d7c0496b
--- /dev/null
+++ b/cmake/Findplutosvg.cmake
@@ -0,0 +1,45 @@
+
+include(FindPackageHandleStandardArgs)
+
+find_package(PkgConfig QUIET)
+pkg_check_modules(PC_PLUTOSVG QUIET plutosvg)
+
+find_library(plutosvg_LIBRARY
+    NAMES plutosvg
+    HINTS ${PC_PLUTOSVG_LIBDIR}
+)
+
+find_path(plutosvg_INCLUDE_PATH
+    NAMES plutosvg.h
+    HINTS ${PC_PLUTOSVG_INCLUDEDIR}
+)
+
+if(PC_PLUTOSVG_FOUND)
+    get_flags_from_pkg_config("${plutosvg_LIBRARY}" "PC_PLUTOSVG" "_plutosvg")
+endif()
+
+set(plutosvg_COMPILE_OPTIONS "${_plutosvg_compile_options}" CACHE STRING "Extra compile flags of plutosvg")
+
+set(plutosvg_LINK_LIBRARIES "${_plutosvg_link_libraries}" CACHE STRING "Extra link libraries of plutosvg")
+
+set(plutosvg_LINK_OPTIONS "${_plutosvg_link_options}" CACHE STRING "Extra link flags of plutosvg")
+
+set(plutosvg_LINK_DIRECTORIES "${_plutosvg_link_directories}" CACHE STRING "Extra link flags of plutosvg")
+
+find_package_handle_standard_args(plutosvg
+    REQUIRED_VARS plutosvg_LIBRARY plutosvg_INCLUDE_PATH
+)
+
+if(plutosvg_FOUND)
+  if(NOT TARGET plutosvg::plutosvg)
+    add_library(plutosvg::plutosvg UNKNOWN IMPORTED)
+    set_target_properties(plutosvg::plutosvg PROPERTIES
+        IMPORTED_LOCATION "${plutosvg_LIBRARY}"
+        INTERFACE_INCLUDE_DIRECTORIES "${plutosvg_INCLUDE_PATH}"
+        INTERFACE_COMPILE_OPTIONS "${plutosvg_COMPILE_OPTIONS}"
+        INTERFACE_LINK_LIBRARIES "${plutosvg_LINK_LIBRARIES}"
+        INTERFACE_LINK_OPTIONS "${plutosvg_LINK_OPTIONS}"
+        INTERFACE_LINK_DIRECTORIES "${plutosvg_LINK_DIRECTORIES}"
+    )
+  endif()
+endif()
diff --git a/cmake/PkgConfigHelper.cmake b/cmake/PkgConfigHelper.cmake
new file mode 100644
index 00000000..7070fac7
--- /dev/null
+++ b/cmake/PkgConfigHelper.cmake
@@ -0,0 +1,34 @@
+# Helper for Find modules
+
+function(get_flags_from_pkg_config _library _pc_prefix _out_prefix)
+  if("${_library}" MATCHES "${CMAKE_STATIC_LIBRARY_SUFFIX}$")
+    set(_cflags ${_pc_prefix}_STATIC_CFLAGS_OTHER)
+    set(_link_libraries ${_pc_prefix}_STATIC_LIBRARIES)
+    set(_link_options ${_pc_prefix}_STATIC_LDFLAGS_OTHER)
+    set(_library_dirs ${_pc_prefix}_STATIC_LIBRARY_DIRS)
+  else()
+    set(_cflags ${_pc_prefix}_CFLAGS_OTHER)
+    set(_link_libraries ${_pc_prefix}_LIBRARIES)
+    set(_link_options ${_pc_prefix}_LDFLAGS_OTHER)
+    set(_library_dirs ${_pc_prefix}_LIBRARY_DIRS)
+  endif()
+
+  # The *_LIBRARIES lists always start with the library itself
+  list(POP_FRONT "${_link_libraries}")
+
+  # Work around CMake's flag deduplication when pc files use `-framework A` instead of `-Wl,-framework,A`
+  string(REPLACE "-framework;" "-Wl,-framework," "_filtered_link_options" "${${_link_options}}")
+
+  set(${_out_prefix}_compile_options
+      "${${_cflags}}"
+      PARENT_SCOPE)
+  set(${_out_prefix}_link_libraries
+      "${${_link_libraries}}"
+      PARENT_SCOPE)
+  set(${_out_prefix}_link_options
+      "${_filtered_link_options}"
+      PARENT_SCOPE)
+  set(${_out_prefix}_link_directories
+      "${${_library_dirs}}"
+      PARENT_SCOPE)
+endfunction()
diff --git a/cmake/SDL3_ttfConfig.cmake.in b/cmake/SDL3_ttfConfig.cmake.in
index 4e0c6281..fc575e02 100644
--- a/cmake/SDL3_ttfConfig.cmake.in
+++ b/cmake/SDL3_ttfConfig.cmake.in
@@ -10,10 +10,12 @@ set(SDL3_ttf_FOUND ON)
 
 set(SDLTTF_VENDORED  @SDLTTF_VENDORED@)
 
-set(SDLTTF_HARFBUZZ @SDLTTF_HARFBUZZ@)
-set(SDLTTF_FREETYPE @SDLTTF_FREETYPE@)
+set(SDLTTF_PLUTOSVG @SDLTTF_PLUTOSVG_ENABLED@)
+set(SDLTTF_HARFBUZZ @SDLTTF_HARFBUZZ_ENABLED@)
+set(SDLTTF_FREETYPE @SDLTTF_FREETYPE_ENABLED@)
 
-set(SDLTTF_SDL3_REQUIRED_VERSION  @SDL_REQUIRED_VERSION@)
+set(SDLTTF_HARFBUZZ_REQUIRED_VERSION    @HARFBUZZ_REQUIRED_VERSION@)
+set(SDLTTF_SDL3_REQUIRED_VERSION        @SDL_REQUIRED_VERSION@)
 
 if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/SDL3_ttf-shared-targets.cmake")
     include("${CMAKE_CURRENT_LIST_DIR}/SDL3_ttf-shared-targets.cmake")
@@ -30,18 +32,24 @@ if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/SDL3_ttf-static-targets.cmake")
             message(WARNING "CXX language not enabled. Linking to SDL3_ttf::SDL3_ttf-static might fail.")
         endif()
     else()
-        include(CMakeFindDependencyMacro)
-
         set(_sdl_cmake_module_path "${CMAKE_MODULE_PATH}")
         list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")
 
-        if(SDLTTF_FREETYPE)
+        include(CMakeFindDependencyMacro)
+        include(PkgConfigHelper)
+
+        if(SDLTTF_FREETYPE AND NOT Freetype::Freetype)
             find_dependency(Freetype)
         endif()
 
-        if(SDLTTF_HARFBUZZ)
+        if(SDLTTF_HARFBUZZ AND NOT harfbuzz::harfbuzz)
             list(APPEND harfbuzz_ROOT "${CMAKE_CURRENT_LIST_DIR}")
-            find_dependency(harfbuzz)
+            find_dependency(harfbuzz ${SDLTTF_HARFBUZZ_REQUIRED_VERSION})
+        endif()
+
+        if(SDLTTF_PLUTOSVG AND NOT plutosvg::plutosvg)
+            list(APPEND plutosvg_ROOT "${CMAKE_CURRENT_LIST_DIR}")
+            find_dependency(plutosvg)
         endif()
 
         set(CMAKE_MODULE_PATH "${_sdl_cmake_module_path}")