Cannot run basic embedded Julia example

I am trying the basic example of embedding Julia in C:

file a.c contains

#include <julia.h>

int main(void) {
    jl_init();
    (void) jl_eval_string("print(sqrt(2.))");
    return 0;
}

and the Makefile is also copy-pasted from the example:

JL_SHARE = $(shell julia -e 'print(joinpath(Sys.BINDIR, Base.DATAROOTDIR, "julia"))')
CFLAGS   += $(shell $(JL_SHARE)/julia-config.jl --cflags)
CXXFLAGS += $(shell $(JL_SHARE)/julia-config.jl --cflags)
LDFLAGS  += $(shell $(JL_SHARE)/julia-config.jl --ldflags)
LDLIBS   += $(shell $(JL_SHARE)/julia-config.jl --ldlibs)
all: a

The thing compiles fine, yet on execution fails with

ERROR: could not load library "/usr/lib/x86_64-linux-gnu/../bin/../lib/x86_64-linux-gnu/julia/sys.so"
/usr/lib/x86_64-linux-gnu/../bin/../lib/x86_64-linux-gnu/julia/sys.so: cannot open shared object file: No such file or directory

This is a plain Debian testing installation, with Julia from the Debian repository; version 1.5.3. The correct location for sys.so would be /usr/lib/x86_64-linux-gnu/julia/sys.so; yet the path used above has either an extra /bin component, or is lacking one /...

The output of julia-config.jl --allflags seems fine:

 -std=gnu99 -I'/usr/include/julia' -fPIC -L'/usr/lib/x86_64-linux-gnu' -Wl,--export-dynamic -Wl,-rpath,'/usr/lib/x86_64-linux-gnu' -Wl,-rpath,'/usr/lib/x86_64-linux-gnu/julia' -ljulia

and the binary ./a is also able to find libjulia.so.1. So the path confusion likely happens somewhere inside libjulia. What did I do wrong, and how can I fix this?

(apart from creating a stupid symlink to add a file in the wrong place in the filesystem, of course)

The julia binary itself, of course, works well and can even correctly locate sys.so:

julia> Libdl.dlpath("sys.so")
"/usr/lib/x86_64-linux-gnu/julia/sys.so"

The safest bet is to either use a Julia you have built from source or an official binary from the Julia Downloads web page.

The problem with the Debian Julia installation is that is uses a file layout that doesn’t match some of the assumptions of the embedding functionality, such as ../bin moving from the directory of libjulia to the directory of julia: https://github.com/JuliaLang/julia/blob/788b2c77c10c2160f4794a4d4b6b81a95a90940c/src/jlapi.c#L94.

If you need to use the Debian Julia installation it might be possible to workaround by replacing jl_init with jl_init_with_image and setting the environment variable JULIA_BINDIR, https://github.com/JuliaLang/julia/blob/788b2c77c10c2160f4794a4d4b6b81a95a90940c/src/init.c#L564.

Relevant reading: https://github.com/JuliaLang/julia/issues/32614#issuecomment-722706116

Thanks for your answer! Since I am trying to (ultimately) add Julia embedding to a program that would run on other people’s computers, I am unable to decide how their Julia directories are laid out.

I managed to pseudo-solve this (at least on my machine) by asking for the location of sys.so at configure time:

 realpath --relative-to=$(dirname $(which julia)) \
  $(julia -e 'using Libdl; print(Libdl.dlpath("sys.so"))')

Then indeed I pass this (and the Julia binary location) to jl_init_with_image(). This is also probably quite robust for porting to another computer, as long as that computer has a working Julia binary and is able to find sys.so.

Maybe something comparable to that fix this should be ported into the jl_init() function? it is quite simple (assuming that replicating the Libdl functionality in C is), does not make any assumptions about the layout (as long as the julia binary works, this should too), and solves some portability issues. (I would also argue that solving this on the Julia side rather than the Debian one would be better; fixing the Debian side would probably be a matter of changing another hardcoded relative path somewhere, while fixing this would reduce the number of hardcoded relative paths, and thus be more robust to future changes).