Skip to content

Commit ca35416

Browse files
committed
meson: Add support for relative rpaths, fixing tests on MacOS w/ SIP
1 parent d54529f commit ca35416

File tree

3 files changed

+145
-14
lines changed

3 files changed

+145
-14
lines changed

meson.build

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ cdata.set_quoted('CONFIGURE_ARGS', '')
161161

162162
exesuffix = '' # overridden below where necessary
163163
dlsuffix = '.so' # overridden below where necessary
164+
rpath_origin = '$ORIGIN'
164165
library_path_var = 'LD_LIBRARY_PATH'
165166

166167
# Format of file to control exports from libraries, and how to pass them to
@@ -207,6 +208,7 @@ if host_system == 'cygwin'
207208
elif host_system == 'darwin'
208209
dlsuffix = '.dylib'
209210
library_path_var = 'DYLD_LIBRARY_PATH'
211+
rpath_origin = '@loader_path'
210212

211213
export_file_format = 'darwin'
212214
export_fmt = '-Wl,-exported_symbols_list,@0@'
@@ -255,8 +257,16 @@ elif host_system == 'netbsd'
255257
# LDFLAGS.
256258
ldflags += ['-Wl,-z,now', '-Wl,-z,relro']
257259

260+
# netbsd patched their meson in a broken way:
261+
# https://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=56959
262+
# until there's a way out of that, disable rpath_origin
263+
rpath_origin = ''
264+
258265
elif host_system == 'openbsd'
259-
# you're ok
266+
# openbsd's $ORIGIN doesn't use an absolute path to the binary, but argv[0]
267+
# (i.e. absolute when invoked with an absolute name, but e.g. not absolute
268+
# when invoked via PATH search).
269+
rpath_origin = ''
260270

261271
elif host_system == 'sunos'
262272
portname = 'solaris'
@@ -268,6 +278,7 @@ elif host_system == 'windows'
268278
exesuffix = '.exe'
269279
dlsuffix = '.dll'
270280
library_path_var = ''
281+
rpath_origin = ''
271282
if cc.get_id() != 'msvc'
272283
# define before including <time.h> for getting localtime_r() etc. on MinGW
273284
cppflags += '-D_POSIX_C_SOURCE'
@@ -2813,25 +2824,41 @@ bin_install_rpaths = []
28132824
lib_install_rpaths = []
28142825
mod_install_rpaths = []
28152826

2816-
2817-
# Don't add rpaths on darwin for now - as long as only absolute references to
2818-
# libraries are needed, absolute LC_ID_DYLIB ensures libraries can be found in
2819-
# their final destination.
2827+
# Add extra_lib_dirs to rpath. This ensures we find libraries we depend on.
2828+
#
2829+
# Not needed on darwin, even if we use relative rpaths for our own libraries,
2830+
# as the install_name of libraries in extra_lib_dirs will point to their
2831+
# location anyway.
28202832
if host_system != 'darwin'
2833+
bin_install_rpaths += postgres_lib_d
2834+
lib_install_rpaths += postgres_lib_d
2835+
mod_install_rpaths += postgres_lib_d
2836+
endif
2837+
2838+
# If the host can form relative rpaths, use that to make the installation
2839+
# properly relocatable
2840+
if rpath_origin != ''
2841+
# PG binaries might need to link to libpq, use relative path to reference
2842+
bin_to_lib = run_command(python, files('src/tools/relpath.py'),
2843+
dir_bin, dir_lib, check: true).stdout().strip()
2844+
bin_install_rpaths += rpath_origin / bin_to_lib
2845+
2846+
# PG extensions might need to link to libpq, use relative path to reference
2847+
# (often just .)
2848+
mod_to_lib = run_command(python, files('src/tools/relpath.py'),
2849+
dir_lib_pkg, dir_lib, check: true).stdout().strip()
2850+
mod_install_rpaths += rpath_origin / mod_to_lib
2851+
2852+
test_use_library_path_var = false
2853+
else
2854+
28212855
# Add absolute path to libdir to rpath. This ensures installed binaries /
28222856
# libraries find our libraries (mainly libpq).
28232857
bin_install_rpaths += dir_prefix / dir_lib
28242858
lib_install_rpaths += dir_prefix / dir_lib
28252859
mod_install_rpaths += dir_prefix / dir_lib
28262860

2827-
# Add extra_lib_dirs to rpath. This ensures we find libraries we depend on.
2828-
#
2829-
# Not needed on darwin even if we use relative rpaths for our own libraries,
2830-
# as the install_name of libraries in extra_lib_dirs will point to their
2831-
# location anyway.
2832-
bin_install_rpaths += postgres_lib_d
2833-
lib_install_rpaths += postgres_lib_d
2834-
mod_install_rpaths += postgres_lib_d
2861+
test_use_library_path_var = true
28352862
endif
28362863

28372864

@@ -3180,6 +3207,14 @@ above, or by running configure and then make maintainer-clean.
31803207
endif
31813208

31823209

3210+
# To make MacOS installation work without a prior make install, even with SIP
3211+
# enabled, make rpaths relative after installation. This also makes the
3212+
# installation relocatable.
3213+
if host_system == 'darwin'
3214+
meson.add_install_script('src/tools/relativize_shared_library_references')
3215+
endif
3216+
3217+
31833218

31843219
###############################################################
31853220
# Install targets
@@ -3310,7 +3345,9 @@ test_env.set('PG_TEST_EXTRA', get_option('PG_TEST_EXTRA'))
33103345
# Add the temporary installation to the library search path on platforms where
33113346
# that works (everything but windows, basically). On windows everything
33123347
# library-like gets installed into bindir, solving that issue.
3313-
if library_path_var != ''
3348+
# On platforms without $ORIGIN support we need to add the temporary
3349+
# installation to the library search path.
3350+
if library_path_var != '' and library_path_var != ''
33143351
test_env.prepend(library_path_var, temp_install_libdir)
33153352
endif
33163353

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#!/usr/bin/env python3
2+
# -*-python-*-
3+
4+
# This script updates a macos postgres installation to reference all internal
5+
# shared libraries using rpaths, leaving absolute install_names in the
6+
# libraries themselves intact.
7+
8+
import os
9+
import sys
10+
import json
11+
import subprocess
12+
import shutil
13+
14+
15+
def installed_path(destdir, path):
16+
if destdir is not None:
17+
return f'{destdir}{path}'
18+
else:
19+
return path
20+
21+
22+
def collect_information():
23+
shared_libraries = []
24+
executables = []
25+
shared_modules = []
26+
27+
meson_info_p = os.path.join(build_root, 'meson-info')
28+
targets = json.load(
29+
open(os.path.join(meson_info_p, 'intro-targets.json')))
30+
installed = json.load(
31+
open(os.path.join(meson_info_p, 'intro-installed.json')))
32+
33+
for target in targets:
34+
if not target['installed']:
35+
continue
36+
37+
filenames = target['filename']
38+
39+
if target['type'] == 'shared library':
40+
assert(len(filenames) == 1)
41+
filename = filenames[0]
42+
43+
shared_libraries.append(installed[filename])
44+
45+
if target['type'] == 'executable':
46+
assert(len(filenames) == 1)
47+
filename = filenames[0]
48+
executables.append(installed[filename])
49+
50+
if target['type'] == 'shared module':
51+
assert(len(filenames) == 1)
52+
filename = filenames[0]
53+
shared_modules.append(installed[filename])
54+
55+
return shared_libraries, executables, shared_modules
56+
57+
58+
def patch_references(destdir, shared_libraries, executables, shared_modules):
59+
install_name_tool = [shutil.which('install_name_tool')]
60+
61+
for lib in shared_libraries:
62+
libname = os.path.basename(lib)
63+
libpath = installed_path(destdir, lib)
64+
newref = f'@rpath/{libname}'
65+
66+
for patch in shared_modules + executables:
67+
patchpath = installed_path(destdir, patch)
68+
69+
# print(f'in {patchpath} replace reference to {libpath} with {newref}')
70+
if not os.path.exists(patchpath):
71+
print(f"path {patchpath} doesn't exist", file=sys.stderr)
72+
sys.exit(1)
73+
74+
cmd = install_name_tool + ['-change', lib, newref, patchpath]
75+
subprocess.check_call(cmd)
76+
77+
78+
if __name__ == '__main__':
79+
build_root = os.environ['MESON_BUILD_ROOT']
80+
destdir = os.environ.get('DESTDIR', None)
81+
82+
print(f'making references to shared libraries relative, destdir is {destdir}',
83+
file=sys.stderr)
84+
85+
shared_libraries, executables, shared_modules = collect_information()
86+
patch_references(destdir, shared_libraries, executables, shared_modules)
87+
88+
sys.exit(0)

src/tools/relpath.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/usr/bin/env python3
2+
3+
import os
4+
import sys
5+
6+
print(os.path.relpath(sys.argv[2], start=sys.argv[1]))

0 commit comments

Comments
 (0)