Embedding Jullia, jl_init(), julia_init() and JULIA_HOME

embedding

#1

I need a bit of help from the experts. I’m continuing Joosep Pata’s work on ROOT.jl, a package that makes it possible to use CERN ROOT from Julia (via Cxx.jl). Cxx.jl alone is not enough, however, because ROOT-6 brings it’s own LLVM-based C++ backend, and really doesn’t like to see Julia’s LLVM instance. Joosep managed to work around the problem by making ROOT.jl create a special Julia executable that first initializes ROOT and then starts Julia: https://github.com/JuliaHEP/ROOT.jl/blob/master/deps/ui.cc (for the history, see this thread.)

I’m trying to update this for Julia v0.6 - and it almost works as it is, but I ran into a problem with JULIA_HOME: If I use the default Julia executable, JULIA_HOME is set correctly (naturally). If I copy the standard Julia executable to a different path and set JULIA_HOME via the environment variable $JULIA_HOME, everything is fine too, of course. But if I run the Julia executable built from ROOT.jl’s “ui.cc”, then JULIA_HOME is set to
"[…]/julia/usr/lib" instead of “[…]/julia/usr/bin” and $JULIA_HOME from the environment is ignored. As a result of the wrong value of JULIA_HOME (lib dir instead of bin dir), package pre-compilation fails when triggered from the ROOT-compatible Julia executable.

I’ve narrowed this down to the fact that ROOT.jl’s “ui.cc” uses

jl_init()

which calls

jl_init_with_image(libjldir, jl_get_default_sysimg_path())

(in “repl.c”) which in turn sets jl_options.julia_home to libjldir. This is a bit confusing, actually, as the first argument of jl_init_with_image is actually named julia_home_dir - I don’t quite get why libjldir passed as the value. Then, when jl_resolve_sysimg_location is run, it won’t touch jl_options.julia_home because it’s already set.

The default Julia executable, however, uses

julia_init(jl_options.image_file_specified ? JL_IMAGE_CWD : JL_IMAGE_JULIA_HOME)

which bypasses jl_init and jl_init_with_image, so that jl_resolve_sysimg_location finds jl_options.julia_home unset and then sets it correctly.

So, here’s my two questions for the experts:

  • Is the behavior of jl_init to ignore JULIA_HOME and set jl_options.julia_home to the lib instead of the bin directory intentional? Somehow this was different in v0.5.

  • Should I use jl_init (according to the “Embedding Julia” section in the Julia docs) or julia_init (and if the latter, what do I need to link in to get access to jl_options)?

Any advice would be very much appreciated.


#2

@yuyichao, you helped Joosep with the initial concept - could you give me a hint?


#3

In 0.6, jl_init() is supposed to resolve the system image location automatically relative to the location of libjulia, via jl_get_default_sysimg_path() uses, which is set in the build system by JL_SYSTEM_IMAGE_PATH.

If you want to use a different sysimg location, you need to use jl_init_with_image() (I believe this is mentioned in the docs).

There’s a long history of confusion about what the meaning of “HOME” is, unfortunately… libjldir in this case is the directory path of libjulia (look at the resolution code in jl_init().

Hopefully it’s not necessary, but if you decided you need that for some reason, you could use dlsym to get the address of the global. Note that it’s not a public API so the layout could change, which should be fine because you are linking julia.h (presumably consistently with the libjulia you are opening…)

Related: https://github.com/JuliaLang/julia/pull/21299


#4

@ihnorton, thanks for the quick anwer!

In 0.6, jl_init() is supposed to resolve the system image location automatically relative to the location of libjulia

But shouldn’t it still set JULIA_HOME the same way it’s normally set (to the bin directory)?

There’s a long history of confusion about what the meaning of “HOME” is, unfortunately

Well, Julia package precompilation seems to have a very clear opinion on it, because it uses JULIA_HOME to construct the path to a Julia binary it wants to start … so, if JULIA_HOME is set to a lib directory, Julia won’t work (at least packages precompilation won’t):

ERROR: could not spawn `[...]/julia/usr/lib/julia -Cnative -J[...]/julia/usr/lib/julia/sys.so --compile=yes --depwarn=yes -O0 --output-ji /user/.julia/lib/v0.6/Compat.ji --output-incremental=yes --startup-file=no --history-file=no --color=no --eval 'while !eof(STDIN)
    eval(Main, deserialize(STDIN))

Won’t that cause problems when embedding Julia, in general?

In any case, if I understand you correctly, I should use julia_init, not jl_init? I’m just asking because the recently updates official embedding example does use jl_init(), so I’m a bit confused.


#5

Ok, I think I get it. This is kind of wonky because JULIA_HOME environment variable is the path of the julia executable – which may or may not exist in an embedding situation. Even if it can’t be assumed, we should probably try harder for the sake of precompilation.

I re-opened this related issue which has a nice reduced case already (easier than installing ROOT). Please comment there if needed:

(I still think you should use only jl_init or jl_init_with_image rather than julia_init, but we can hopefully do a better job by default for jl_init)


#6

I just want to say that I’m really happy you are resuming work on ROOT.jl :smile:.

I graduated in late 2015 and wound up working as a data scientist, having not discovered Julia until early 2016, and it saddens me deeply that I was not aware of Julia (and it was not in a mature state, as it sort of is now) when I was still doing physics. Sometimes I feel like all the wonderful things I’ve discovered about Julia are going to waste now that I’m not doing physics. During most of the time, and at most of the places I was a student the attitude was typically “anything other than C++ or Fortran is too slow and/or is silly and/or is useless” (an attitude I was an exponent of, as there weren’t really any alternatives at the time), but, somewhat to my horror, by the time I left people were starting to use Python. (Though, at least the people I knew were actually using it as a scripting language, like it is supposed to be, unlike in the data science community where it somehow gets used for everything under the sun.) When I imagine reliving all my physics days with Julia I am filled with joy, nostalgia, and enthusiasm for better ways of doing things.

Sorry, I know that was a huge tangent from the subject of this post, I just wanted to share my enthusiasm about ROOT.jl. I would love to be of help some time if I have the time.


#7

Thanks very much for looking into this, @ihnorton!

I have to admit that now that I’m using julia_init, I kinda like it for this special case, though - I want the root-enabled julia executable to be as close to the standard julia exe as possible, including honoring $JULIA_HOME, if set.