Re-do and clean up CMake support, making it use relocatable paths
Closes #4025.
diff --git a/meson.build b/meson.build
index 324d1dd..fc0892b 100644
--- a/meson.build
+++ b/meson.build
@@ -385,6 +385,26 @@
endif
endforeach
+# CMake support (package install dir)
+
+# Equivalent to configure_package_config_file(INSTALL_DESTINATION ...), see
+# https://cmake.org/cmake/help/latest/module/CMakePackageConfigHelpers.html#command:configure_package_config_file.
+# In certain unusual packaging layouts such as Nixpkgs, the Harfbuzz package
+# is installed into two Nix store paths, "out" and "dev", where "out" contains
+# libraries only (i.e. lib/libharfbuzz.so) and "dev" contains development
+# files, i.e. include and lib/cmake. If CMake package files are installed to
+# "out", Nixpkgs will move them to "dev", which breaks assumptions about
+# our file paths. Since we need to figure out relative install paths here
+# to make a relocatable package, we do need to know the final path of our
+# CMake files to calculate the correct relative paths.
+# Of course, this still defaults to $libdir/cmake if unset, which works for
+# most packaging layouts.
+cmake_package_install_dir = get_option('cmakepackagedir')
+
+if cmake_package_install_dir == ''
+ cmake_package_install_dir = get_option('libdir') / 'cmake'
+endif
+
subdir('src')
if not get_option('utilities').disabled()
@@ -415,6 +435,7 @@
'libdir': get_option('libdir'),
'includedir': get_option('includedir'),
'datadir': get_option('datadir'),
+ 'cmakepackagedir': cmake_package_install_dir
},
'Unicode callbacks (you want at least one)':
{'Builtin': true,
diff --git a/meson_options.txt b/meson_options.txt
index ace50eb..97d6dae 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -46,3 +46,7 @@
description: 'Build Ragel subproject if no suitable version is found')
option('fuzzer_ldflags', type: 'string',
description: 'Extra LDFLAGS used during linking of fuzzing binaries')
+
+# Install directory options
+option('cmakepackagedir', type: 'string',
+ description: 'CMake package configuration install directory')
diff --git a/src/Makefile.am b/src/Makefile.am
index 430d44a..bc2a3de 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -14,7 +14,7 @@
EXTRA_DIST += harfbuzz.cc harfbuzz-subset.cc
EXTRA_DIST += meson.build
-EXTRA_DIST += fix_get_types.py
+EXTRA_DIST += fix_get_types.py relative_to.py
# Convenience targets:
lib: $(BUILT_SOURCES) libharfbuzz.la
diff --git a/src/harfbuzz-config.cmake.in b/src/harfbuzz-config.cmake.in
index b22077d..6abe2d6 100644
--- a/src/harfbuzz-config.cmake.in
+++ b/src/harfbuzz-config.cmake.in
@@ -1,97 +1,32 @@
-# Set these variables so that the `${prefix}/lib` expands to something we can
-# remove.
-set(_harfbuzz_remove_string "REMOVE_ME")
-set(exec_prefix "${_harfbuzz_remove_string}")
-set(prefix "${_harfbuzz_remove_string}")
+@PACKAGE_INIT@
-# Compute the installation prefix by stripping components from our current
-# location.
-get_filename_component(_harfbuzz_prefix "${CMAKE_CURRENT_LIST_DIR}" DIRECTORY)
-get_filename_component(_harfbuzz_prefix "${_harfbuzz_prefix}" DIRECTORY)
-set(_harfbuzz_libdir "@libdir@")
-string(REPLACE "${_harfbuzz_remove_string}/" "" _harfbuzz_libdir "${_harfbuzz_libdir}")
-set(_harfbuzz_libdir_iter "${_harfbuzz_libdir}")
-while (_harfbuzz_libdir_iter)
- set(_harfbuzz_libdir_prev_iter "${_harfbuzz_libdir_iter}")
- get_filename_component(_harfbuzz_libdir_iter "${_harfbuzz_libdir_iter}" DIRECTORY)
- if (_harfbuzz_libdir_prev_iter STREQUAL _harfbuzz_libdir_iter)
- break()
- endif ()
- get_filename_component(_harfbuzz_prefix "${_harfbuzz_prefix}" DIRECTORY)
-endwhile ()
-unset(_harfbuzz_libdir_iter)
-
-# Get the include subdir.
-set(_harfbuzz_includedir "@includedir@")
-string(REPLACE "${_harfbuzz_remove_string}/" "" _harfbuzz_includedir "${_harfbuzz_includedir}")
-
-# Extract version information from libtool.
-set(_harfbuzz_version_info "@HB_LIBTOOL_VERSION_INFO@")
-string(REPLACE ":" ";" _harfbuzz_version_info "${_harfbuzz_version_info}")
-list(GET _harfbuzz_version_info 0
- _harfbuzz_current)
-list(GET _harfbuzz_version_info 1
- _harfbuzz_revision)
-list(GET _harfbuzz_version_info 2
- _harfbuzz_age)
-unset(_harfbuzz_version_info)
-
-if ("@default_library@" MATCHES "static")
- set(_harfbuzz_lib_prefix "lib")
- set(_harfbuzz_lib_suffix ".a")
-else ()
- if (APPLE)
- set(_harfbuzz_lib_prefix "${CMAKE_SHARED_LIBRARY_PREFIX}")
- set(_harfbuzz_lib_suffix ".0${CMAKE_SHARED_LIBRARY_SUFFIX}")
- elseif (UNIX)
- set(_harfbuzz_lib_prefix "${CMAKE_SHARED_LIBRARY_PREFIX}")
- set(_harfbuzz_lib_suffix "${CMAKE_SHARED_LIBRARY_SUFFIX}.0.${_harfbuzz_current}.${_harfbuzz_revision}")
- elseif (WIN32)
- set(_harfbuzz_lib_prefix "${CMAKE_IMPORT_LIBRARY_PREFIX}")
- set(_harfbuzz_lib_suffix "${CMAKE_IMPORT_LIBRARY_SUFFIX}")
- else ()
- # Unsupported.
- set(harfbuzz_FOUND 0)
- endif ()
-endif ()
+set_and_check(HARFBUZZ_INCLUDE_DIR "@PACKAGE_INCLUDE_INSTALL_DIR@")
# Add the libraries.
-add_library(harfbuzz::harfbuzz SHARED IMPORTED)
+add_library(harfbuzz::harfbuzz @HB_LIBRARY_TYPE@ IMPORTED)
set_target_properties(harfbuzz::harfbuzz PROPERTIES
- INTERFACE_INCLUDE_DIRECTORIES "${_harfbuzz_prefix}/${_harfbuzz_includedir}/harfbuzz"
- IMPORTED_LOCATION "${_harfbuzz_prefix}/${_harfbuzz_libdir}/${_harfbuzz_lib_prefix}harfbuzz${_harfbuzz_lib_suffix}")
+ INTERFACE_INCLUDE_DIRECTORIES "@PACKAGE_INCLUDE_INSTALL_DIR@"
+ IMPORTED_LOCATION "@PACKAGE_CMAKE_INSTALL_LIBDIR@/@HB_LIB_PREFIX@harfbuzz@HB_LIB_SUFFIX@")
-add_library(harfbuzz::icu SHARED IMPORTED)
+add_library(harfbuzz::icu @HB_LIBRARY_TYPE@ IMPORTED)
set_target_properties(harfbuzz::icu PROPERTIES
- INTERFACE_INCLUDE_DIRECTORIES "${_harfbuzz_prefix}/${_harfbuzz_includedir}/harfbuzz"
+ INTERFACE_INCLUDE_DIRECTORIES "@PACKAGE_INCLUDE_INSTALL_DIR@"
INTERFACE_LINK_LIBRARIES "harfbuzz::harfbuzz"
- IMPORTED_LOCATION "${_harfbuzz_prefix}/${_harfbuzz_libdir}/${_harfbuzz_lib_prefix}harfbuzz-icu${_harfbuzz_lib_suffix}")
+ IMPORTED_LOCATION "@PACKAGE_CMAKE_INSTALL_LIBDIR@/@HB_LIB_PREFIX@harfbuzz-icu@HB_LIB_SUFFIX@")
-add_library(harfbuzz::subset SHARED IMPORTED)
+add_library(harfbuzz::subset @HB_LIBRARY_TYPE@ IMPORTED)
set_target_properties(harfbuzz::subset PROPERTIES
- INTERFACE_INCLUDE_DIRECTORIES "${_harfbuzz_prefix}/${_harfbuzz_includedir}/harfbuzz"
+ INTERFACE_INCLUDE_DIRECTORIES "@PACKAGE_INCLUDE_INSTALL_DIR@"
INTERFACE_LINK_LIBRARIES "harfbuzz::harfbuzz"
- IMPORTED_LOCATION "${_harfbuzz_prefix}/${_harfbuzz_libdir}/${_harfbuzz_lib_prefix}harfbuzz-subset${_harfbuzz_lib_suffix}")
+ IMPORTED_LOCATION "@PACKAGE_CMAKE_INSTALL_LIBDIR@/@HB_LIB_PREFIX@harfbuzz-subset@HB_LIB_SUFFIX@")
# Only add the gobject library if it was built.
-set(_harfbuzz_have_gobject "@have_gobject@")
-if (_harfbuzz_have_gobject)
- add_library(harfbuzz::gobject SHARED IMPORTED)
+if (@HB_HAVE_GOBJECT@)
+ add_library(harfbuzz::gobject @HB_LIBRARY_TYPE@ IMPORTED)
set_target_properties(harfbuzz::gobject PROPERTIES
- INTERFACE_INCLUDE_DIRECTORIES "${_harfbuzz_prefix}/${_harfbuzz_includedir}/harfbuzz"
+ INTERFACE_INCLUDE_DIRECTORIES "@PACKAGE_INCLUDE_INSTALL_DIR@"
INTERFACE_LINK_LIBRARIES "harfbuzz::harfbuzz"
- IMPORTED_LOCATION "${_harfbuzz_prefix}/${_harfbuzz_libdir}/${_harfbuzz_lib_prefix}harfbuzz-gobject${_harfbuzz_lib_suffix}")
+ IMPORTED_LOCATION "@PACKAGE_CMAKE_INSTALL_LIBDIR@/@HB_LIB_PREFIX@harfbuzz-gobject@HB_LIB_SUFFIX@")
endif ()
-# Clean out variables we used in our scope.
-unset(_harfbuzz_lib_prefix)
-unset(_harfbuzz_lib_suffix)
-unset(_harfbuzz_current)
-unset(_harfbuzz_revision)
-unset(_harfbuzz_age)
-unset(_harfbuzz_includedir)
-unset(_harfbuzz_libdir)
-unset(_harfbuzz_prefix)
-unset(exec_prefix)
-unset(prefix)
-unset(_harfbuzz_remove_string)
+check_required_components(harfbuzz)
diff --git a/src/meson.build b/src/meson.build
index 91065fb..19f7cf2 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -1,3 +1,5 @@
+fs = import('fs')
+
hb_version_h = configure_file(
command: [find_program('gen-hb-version.py'), meson.project_version(), '@OUTPUT@', '@INPUT@'],
input: 'hb-version.h.in',
@@ -789,15 +791,95 @@
have_gobject = conf.get('HAVE_GOBJECT', 0) == 1
+# This code (especially PACKAGE_INIT) kept similar to what CMake's own
+# configure_package_config_file() generates, see
+# https://cmake.org/cmake/help/latest/module/CMakePackageConfigHelpers.html#command:configure_package_config_file
+
cmake_config = configuration_data()
-cmake_config.set('libdir', '${prefix}/@0@'.format(get_option('libdir')))
-cmake_config.set('includedir', '${prefix}/@0@'.format(get_option('includedir')))
-cmake_config.set('HB_LIBTOOL_VERSION_INFO', hb_libtool_version_info)
-cmake_config.set('have_gobject', '@0@'.format(have_gobject))
+cmake_config_dir = cmake_package_install_dir / 'harfbuzz'
+
+have_fs_relative_to = meson.version().version_compare('>=1.3.0')
+
+if not have_fs_relative_to
+ relative_to = find_program('relative_to.py')
+endif
+
+if have_fs_relative_to
+ cmake_package_prefix_dir = fs.relative_to(get_option('prefix'), get_option('prefix') / cmake_config_dir)
+else
+ cmake_package_prefix_dir = run_command(relative_to, get_option('prefix'), get_option('prefix') / cmake_config_dir, check: true).stdout().strip()
+endif
+
+cmake_package_prefix_dir = '${CMAKE_CURRENT_LIST_DIR}/@0@'.format(cmake_package_prefix_dir)
+
+# Make all the relevant paths relative to our prefix, so we can later append
+# them onto ${PACKAGE_PREFIX_DIR} to get the correct paths.
+
+cmake_install_includedir = get_option('includedir')
+
+if fs.is_absolute(cmake_install_includedir)
+ if have_fs_relative_to
+ cmake_install_includedir = fs.relative_to(cmake_install_includedir, get_option('prefix'))
+ else
+ cmake_install_includedir = run_command(relative_to, cmake_install_includedir, get_option('prefix'), check: true).stdout().strip()
+ endif
+endif
+
+cmake_install_libdir = get_option('libdir')
+
+if fs.is_absolute(cmake_install_libdir)
+ if have_fs_relative_to
+ cmake_install_libdir = fs.relative_to(cmake_install_libdir, get_option('prefix'))
+ else
+ cmake_install_libdir = run_command(relative_to, cmake_install_libdir, get_option('prefix'), check: true).stdout().strip()
+ endif
+endif
+
+cmake_config.set('PACKAGE_INIT', '''
+get_filename_component(PACKAGE_PREFIX_DIR "@0@" ABSOLUTE)
+
+macro(set_and_check _var _file)
+ set(${_var} "${_file}")
+ if(NOT EXISTS "${_file}")
+ message(FATAL_ERROR "File or directory ${_file} referenced by variable ${_var} does not exist !")
+ endif()
+endmacro()
+
+macro(check_required_components _NAME)
+ foreach(comp ${${_NAME}_FIND_COMPONENTS})
+ if(NOT ${_NAME}_${comp}_FOUND)
+ if(${_NAME}_FIND_REQUIRED_${comp})
+ set(${_NAME}_FOUND FALSE)
+ endif()
+ endif()
+ endforeach()
+endmacro()
+'''.format(cmake_package_prefix_dir))
+
+cmake_config.set('PACKAGE_CMAKE_INSTALL_INCLUDEDIR', '${PACKAGE_PREFIX_DIR}/@0@'.format(cmake_install_includedir))
+cmake_config.set('PACKAGE_CMAKE_INSTALL_LIBDIR', '${PACKAGE_PREFIX_DIR}/@0@'.format(cmake_install_libdir))
+cmake_config.set('PACKAGE_INCLUDE_INSTALL_DIR', '${PACKAGE_PREFIX_DIR}/@0@/@1@'.format(cmake_install_includedir, meson.project_name()))
+cmake_config.set('HB_HAVE_GOBJECT', have_gobject ? 'YES' : 'NO')
+cmake_config.set('HB_LIBRARY_TYPE', get_option('default_library') == 'static' ? 'STATIC' : 'SHARED')
+
+if get_option('default_library') == 'static'
+ cmake_config.set('HB_LIB_PREFIX', '${CMAKE_STATIC_LIBRARY_PREFIX}')
+ cmake_config.set('HB_LIB_SUFFIX', '${CMAKE_STATIC_LIBRARY_SUFFIX}')
+elif host_machine.system() == 'darwin'
+ cmake_config.set('HB_LIB_PREFIX', '${CMAKE_SHARED_LIBRARY_PREFIX}')
+ cmake_config.set('HB_LIB_SUFFIX', '.@0@.${CMAKE_SHARED_LIBRARY_SUFFIX}'.format(hb_so_version))
+elif host_machine.system() == 'windows'
+ cmake_config.set('HB_LIB_PREFIX', '${CMAKE_IMPORT_LIBRARY_PREFIX}')
+ cmake_config.set('HB_LIB_SUFFIX', '${CMAKE_IMPORT_LIBRARY_SUFFIX}')
+else
+ cmake_config.set('HB_LIB_PREFIX', '${CMAKE_SHARED_LIBRARY_PREFIX}')
+ cmake_config.set('HB_LIB_SUFFIX', '${CMAKE_SHARED_LIBRARY_SUFFIX}.@0@'.format(version))
+endif
+
configure_file(input: 'harfbuzz-config.cmake.in',
output: 'harfbuzz-config.cmake',
configuration: cmake_config,
- install_dir: get_option('libdir') / 'cmake' / 'harfbuzz',
+ install_dir: cmake_config_dir,
)
gobject_enums_c = []
diff --git a/src/relative_to.py b/src/relative_to.py
new file mode 100755
index 0000000..8a676bf
--- /dev/null
+++ b/src/relative_to.py
@@ -0,0 +1,6 @@
+#!/usr/bin/python3
+
+import sys
+from os import path
+
+print(path.relpath(sys.argv[1], sys.argv[2]))