From c72b9be118c6b5ac2c1c768389da4936d05fe504 Mon Sep 17 00:00:00 2001 From: Andres Freund Date: Sat, 27 Aug 2022 10:23:01 -0700 Subject: [PATCH v12 10/15] meson: Add support for relative rpaths, fixing tests on MacOS w/ SIP --- meson.build | 50 +++++++++-- .../relativize_shared_library_references | 84 +++++++++++++++++++ src/tools/relpath.py | 6 ++ 3 files changed, 135 insertions(+), 5 deletions(-) create mode 100755 src/tools/relativize_shared_library_references create mode 100755 src/tools/relpath.py diff --git a/meson.build b/meson.build index f1ecf685f14..e64054b1c56 100644 --- a/meson.build +++ b/meson.build @@ -152,6 +152,7 @@ portname = host_system exesuffix = '' # overridden below where necessary dlsuffix = '.so' # overridden below where necessary +rpath_origin = '$ORIGIN' library_path_var = 'LD_LIBRARY_PATH' export_file_format = 'gnu' @@ -192,6 +193,7 @@ if host_system == 'aix' elif host_system == 'darwin' dlsuffix = '.dylib' ld_library_path_var = 'DYLD_LIBRARY_PATH' + rpath_origin = '@loader_path' export_file_format = 'darwin' export_fmt = '-exported_symbols_list=@0@' @@ -211,6 +213,7 @@ elif host_system == 'windows' exesuffix = '.exe' dlsuffix = '.dll' ld_library_path_var = '' + rpath_origin = '' export_file_format = 'win' export_file_suffix = 'def' @@ -261,7 +264,17 @@ elif host_system == 'linux' elif host_system == 'cygwin' cppflags += '-D_GNU_SOURCE' -elif host_system in ['freebsd', 'netbsd', 'openbsd'] +elif host_system == 'openbsd' + # openbsd's $ORIGIN doesn't use an absolute path to the binary, but argv[0] + # (i.e. absolute when invoked with an absolute name, but e.g. not absolute + # when invoked via PATH search). + rpath_origin = '' +elif host_system == 'netbsd' + # netbsd patched their meson in a broken way: + # https://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=56959 + # until there's a way out of that, disable rpath_origin + rpath_origin = '' +elif host_system == 'freebsd' # you're ok else # XXX: Should we add an option to override the host_system as an escape @@ -2394,6 +2407,26 @@ bin_install_rpaths = [] lib_install_rpaths = [] mod_install_rpaths = [] + +# If the host can form relative rpaths, use that to make the installation +# properly relocatable +if rpath_origin != '' + # PG binaries might need to link to libpq, use relative path to reference + bin_to_lib = run_command(python, files('src/tools/relpath.py'), + dir_bin, dir_lib, check: true).stdout().strip() + bin_install_rpaths += rpath_origin / bin_to_lib + + # PG extensions might need to link to libpq, use relative path to reference + # (often just .) + mod_to_lib = run_command(python, files('src/tools/relpath.py'), + dir_lib_pkg, dir_lib, check: true).stdout().strip() + mod_install_rpaths += rpath_origin / mod_to_lib + + test_use_library_path_var = false +else + test_use_library_path_var = true +endif + # add extra_lib_dirs to rpath bin_install_rpaths += postgres_lib_d lib_install_rpaths += postgres_lib_d @@ -2650,6 +2683,14 @@ above, or by running configure and then make maintainer clean. endif +# To make MacOS installation work without a prior make install, even with SIP +# enabled, make rpaths relative after installation. This also makes the +# installation relocatable. +if host_system == 'darwin' + meson.add_install_script('src/tools/relativize_shared_library_references') +endif + + ############################################################### # Test prep @@ -2715,10 +2756,9 @@ test_env.set('REGRESS_SHLIB', regress_module.full_path()) # Export PG_TEST_EXTRA so it can be checked in individual tap tests. test_env.set('PG_TEST_EXTRA', get_option('PG_TEST_EXTRA')) -# Add the temporary installation to the library search path on platforms where -# that works (everything but windows, basically). On windows everything -# library-like gets installed into bindir, solving that issue. -if library_path_var != '' +# On platforms without $ORIGIN support we need to add the temporary +# installation to the library search path. +if test_use_library_path_var and library_path_var != '' test_env.prepend(library_path_var, test_install_location / get_option('libdir')) endif diff --git a/src/tools/relativize_shared_library_references b/src/tools/relativize_shared_library_references new file mode 100755 index 00000000000..db6431639f1 --- /dev/null +++ b/src/tools/relativize_shared_library_references @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 +# -*-python-*- + +# This script updates a macos postgres installation to reference all internal +# shared libraries using rpaths, leaving absolute install_names in the +# libraries themselves intact. + +import os +import shlex +import sys +import json +import subprocess +import shutil + + +def installed_path(destdir, path): + if destdir is not None: + return f'{destdir}{path}' + else: + return path + + +def collect_information(): + shared_libraries = [] + executables = [] + shared_modules = [] + + targets = json.load(open(os.path.join(build_root, 'meson-info', 'intro-targets.json'))) + installed = json.load(open(os.path.join(build_root, 'meson-info', 'intro-installed.json'))) + + for target in targets: + if not target['installed']: + continue + + filenames = target['filename'] + + if target['type'] == 'shared library': + assert(len(filenames) == 1) + filename = filenames[0] + + shared_libraries.append(installed[filename]) + + if target['type'] == 'executable': + assert(len(filenames) == 1) + filename = filenames[0] + executables.append(installed[filename]) + + if target['type'] == 'shared module': + assert(len(filenames) == 1) + filename = filenames[0] + shared_modules.append(installed[filename]) + + return shared_libraries, executables, shared_modules + + +def patch_references(destdir, shared_libraries, executables, shared_modules): + install_name_tool = [shutil.which('install_name_tool')] + + for lib in shared_libraries: + libname = os.path.basename(lib) + libpath = installed_path(destdir, lib) + newref = f'@rpath/{libname}' + + for patch in shared_modules + executables: + patchpath = installed_path(destdir, patch) + + #print(f'in {patchpath} replace reference to {libpath} with {newref}') + if not os.path.exists(patchpath): + print(f"path {patchpath} doesn't exist", file=sys.stderr) + sys.exit(1) + + subprocess.check_call(install_name_tool + ['-change', lib, newref, patchpath]) + + +if __name__ == '__main__': + build_root = os.environ['MESON_BUILD_ROOT'] + destdir = os.environ.get('DESTDIR', None) + + print(f'making references to shared libraries relative, destdir is {destdir}', file=sys.stderr) + + shared_libraries, executables, shared_modules = collect_information() + patch_references(destdir, shared_libraries, executables, shared_modules) + + sys.exit(0) diff --git a/src/tools/relpath.py b/src/tools/relpath.py new file mode 100755 index 00000000000..87bcb496ab5 --- /dev/null +++ b/src/tools/relpath.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python3 + +import os +import sys + +print(os.path.relpath(sys.argv[2], start=sys.argv[1])) -- 2.37.0.3.g30cc8d0f14