Skip to content

Commit 22a7e7b

Browse files
committed
Apply fixes to dynamic libraries on macOS
1 parent 395bb9c commit 22a7e7b

File tree

1 file changed

+76
-17
lines changed

1 file changed

+76
-17
lines changed

scripts/build_als.sh

Lines changed: 76 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,8 @@ EOF
147147
alr exec alr -- action -r post-fetch # Configure XmlAda, etc
148148
}
149149

150-
# A temporary file for langkit setevn
151-
SETENV=$PWD/subprojects/libadalang/setenv.sh
150+
# A temporary file for langkit environment
151+
LANGKIT_SETENV=$PWD/subprojects/libadalang/setenv.sh
152152

153153
# Build langkit which is required to generate libadalang.
154154
function build_langkit_raw() {
@@ -160,33 +160,87 @@ function build_langkit_raw() {
160160

161161
sed -i.bak -e 's/GPR_BUILD/GPR_LIBRARY_TYPE/' ./langkit/libmanage.py
162162
pip install .
163+
164+
# On macOS, the full path of gnat.adc is stored in ALI files with case
165+
# normalization. Upon re-runs, gprbuild is unable to match the
166+
# normalized path with a non-case-normalized real path. This causes
167+
# unnecessary recompilations at every run.
168+
#
169+
# To avoid that, we use the -gnateb flag which tells GNAT to not use
170+
# an absolute path for gnat.adc
163171
python manage.py make --no-mypy --generate-auto-dll-dirs \
164-
--library-types=relocatable --gargs "-cargs -fPIC"
172+
--library-types=relocatable --gargs "-m -v -cargs:ada -gnateb"
173+
174+
if [[ $NODE_ARCH_PLATFORM = *darwin* ]]; then
175+
find . -name '*.dylib' -print0 |
176+
while IFS= read -r -d '' lib; do
177+
fix_dylib_rpaths "$lib"
178+
done
179+
fi
165180

166-
# Export the environment to use langkit into a file for later usage
167-
python manage.py setenv >"$SETENV"
181+
# Export the environment needed to use langkit into a file for later
182+
# usage
183+
python manage.py setenv >"$LANGKIT_SETENV"
168184

169185
if [[ $NODE_ARCH_PLATFORM == "x64/win32" ]]; then
170186
# Fix setenv.sh to be bash script for MSYS2 by replacing
171187
# 1) C:\ -> /C/ 2) '\' -> '/' and ';' -> ':' 3) ": export" -> "; export"
172-
sed -i -e 's#\([A-Z]\):\\#/\1/#g' -e 'y#\\;#/:#' -e 's/: export /; export /' "$SETENV"
173-
cat "$SETENV"
188+
sed -i -e 's#\([A-Z]\):\\#/\1/#g' -e 'y#\\;#/:#' -e 's/: export /; export /' "$LANGKIT_SETENV"
174189
fi
175190

176-
# Clean `.ali` and `.o` to avoid static vis relocatable mess
177-
find . -name '*.o' -delete
178-
find . -name '*.ali' -delete
191+
cat "$LANGKIT_SETENV"
179192
)
180193
}
181194

195+
# On macOS, there are two issues with gprbuild:
196+
#
197+
# 1. The linker arguments for rpath produce a leading space into paths:
198+
# "-Wl,-rpath, @executable_path/...". This makes the dynamic library loader
199+
# unable to use those rpaths at runtime.
200+
# 2. The linker uses "@executable_path" which applies in the context of an
201+
# exectuable but not in a context where the library is loaded directly, which
202+
# is precisely the case we want when we do `import liblktlang` in Python.
203+
# Instead, "@loader_path" should be used.
204+
#
205+
# This function applies a workaround by removing the leading space from rpath
206+
# entries, and replacing @executable_path with @loader_path.
207+
function fix_dylib_rpaths() {
208+
lib=$1
209+
# Log the full output of otool for debugging
210+
otool -l "$lib"
211+
212+
# First fix paths with a leading space
213+
paths_with_space=$(otool -l "$lib" | grep -A2 LC_RPATH | grep "path " | awk '{ print $2 }')
214+
for p in $paths_with_space; do
215+
install_name_tool -rpath " $p" "${p/@executable_path/@loader_path}" "$lib"
216+
done
217+
# Then replace @executable_path with @loader_path in all paths (there can be
218+
# ones without a leading space, hence doing 2 passes)
219+
paths_with_exec_path=$(otool -l "$lib" | grep -A2 LC_RPATH | grep "@executable_path" | awk '{ print $2 }')
220+
for p in $paths_with_exec_path; do
221+
install_name_tool -rpath "$p" "${p/@executable_path/@loader_path}" "$lib"
222+
done
223+
}
224+
182225
# Run build_langkit_raw in Alire environment
183226
function build_langkit() {
184227
# We use 'alr exec' to benefit from Alire setting up GPR_PROJECT_PATH with
185228
# all the dependencies.
186229
alr exec bash -- -x "$0" build_langkit_raw
187230
}
188231

189-
function set_langkit_usage_env() {
232+
# This function adds the paths of DLLs from GCC installation and the Alire deps
233+
# in alire/cache/dependencies (i.e. Alire deps that haven't been pinned to
234+
# local checkouts) to the runtime environement so that they may be loaded in
235+
# cases where the DLLs we build don't have appropriate RPATH entries.
236+
#
237+
# This is necessary on Windows and macOS, so PATH and DYLD_LIBRARY_PATH are
238+
# used.
239+
#
240+
# However, on macOS we're now using fix_dylib_rpaths to correct the RPATH
241+
# entries, so DYLD_LIBRARY_PATH should no longer be necessary. But we keep it
242+
# for good measure.
243+
function add_unpinned_deps_dlls_to_runtime_path() {
190244
ADALIB=$(alr exec gcc -- -print-libgcc-file-name)
191245

192246
if [[ $NODE_ARCH_PLATFORM == "x64/win32" ]]; then
@@ -210,19 +264,24 @@ function set_langkit_usage_env() {
210264
echo "NEW_PATH=$NEW_PATH"
211265
export DYLD_LIBRARY_PATH=$NEW_PATH:$DYLD_LIBRARY_PATH
212266
export PATH=$NEW_PATH":$PATH"
213-
214-
source "$SETENV"
215267
}
216268

217269
# Build ALS with alire
218270
function build_als() {
219-
# Check if langkit is usable
220-
set_langkit_usage_env
221-
python -c 'import liblktlang'
271+
add_unpinned_deps_dlls_to_runtime_path
272+
273+
# Check that we can use langkit successfully
274+
(
275+
source "$LANGKIT_SETENV"
276+
# On Windows it is not enough to source the langkit env and unpinned
277+
# deps. The libraries of pinned Alire dependencies (not under
278+
# alire/cache/dependencies) must also be made visible.
279+
alr exec python -- -c 'import liblktlang; print("Imported liblktlang successfully")'
280+
)
222281

223282
# We use 'alr exec' to benefit from Alire setting up GPR_PROJECT_PATH with
224283
# all the dependencies.
225-
LIBRARY_TYPE=static STANDALONE=no GPRBUILD_CARGS="$gprbuild_flag" alr exec make -- "VERSION=$TAG" all
284+
LIBRARY_TYPE=static STANDALONE=no alr exec make -- "VERSION=$TAG" all
226285
}
227286

228287
function test_als() {

0 commit comments

Comments
 (0)