Loading a library which depends on another library

I have a project that is a mixture between Julia and Lean. The Lean part builds a dynamic library at .lake/build/lib/libPlaceholder.{dylib,so}, which in turn depends on the system Lean installation at ~/.elan/toolchains/leanprover--lean4---v4.21.0/lib/lean/libLean_shared.{dylib,so}.

I’m loading the library like this:

using Libdl

push!(Base.DL_LOAD_PATH, "~/.elan/toolchains/leanprover--lean4---v4.21.0/lib/lean")
push!(Base.DL_LOAD_PATH, "./.lake/build/lib")

lib = dlopen("libPlaceholder")

which complains that

dlopen(./.lake/build/lib/libPlaceholder.dylib, 0x0001): Library not loaded: @rpath/libLake_shared.dylib
  Referenced from: <4C4C447C-5555-3144-A17F-23FAD48EFDC2> $PROJECT/.lake/build/lib/libPlaceholder.dylib (built for macOS 99.0 which is newer than running OS)
  Reason: tried: '/Users/aniva/.julia/juliaup/julia-1.11.6+0.aarch64.apple.darwin14/lib/julia/libLake_shared.dylib' (no such file), '/Users/aniva/.julia/juliaup/julia-1.11.6+0.aarch64.apple.darwin14/lib/julia/../libLake_shared.dylib' (no such file), '/Users/aniva/.julia/juliaup/julia-1.11.6+0.aarch64.apple.darwin14/lib/libLake_shared.dylib' (no such file)

Notably, it is not looking for the dylib in question in ~/.elan/toolchains/leanprover--lean4---v4.21.0/lib/lean.

Is modifying LD_LIBRARY_PATH the only way to resolve this problem? What is the best practice for loading dynamic libraries like these?

Edit: The same code works well on Linux, but does not work on MacOS.

DYLD_LIBRARY_PATH="$(lake env printenv LEAN_SYSROOT)/lib/lean" julia src/primitive.jl

works for MacOS though.

I’m no expert but I think maybe if you do dlopen("libLean_shared") first, with the right flags, then the system remembers where this library is when it needs to load it again.

(I think the issue that you’re experiencing is that DL_LOAD_PATH is only used to find libraries loaded directly by dlopen, not ones that those load.)

In particular, from the dlopen documentation:

For instance RTLD_LAZY|RTLD_DEEPBIND|RTLD_GLOBAL allows the library’s symbols to be available for usage in other shared libraries, addressing situations where there are dependencies between shared libraries.