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.
154154function 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
183226function 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
218270function 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
228287function test_als() {
0 commit comments