NIF - Any way to set LD_LIBRARY_PATH during compilation?

I’m in the process of writing a NIF library application which in turn depends on a vendor’s shared lib (which has it’s own set of dependencies as well). My hope is to just keep all the various .so’s contained in /priv.

I’m using elixir_make to build my NIF’s code into it’s .so, which is also spit out into /priv by the makefile.

If I just run make, my /priv folder contains vendor.so, vendor_dep1.so, my_app_nif.so, which are all the files I would expect to be present.

I can successfully compile the elixir side of my application with: LD_LIBRARY_PATH=./priv mix compile, but I was wondering if there was a good way to set the LD_LIBRARY_PATH variable from within the project, so another user can just mix compile and it just works? Otherwise mix compile fails when compiling my nif module due to the @on_load bits normally set when writing nifs:

defmodule MyApp.Nif do
  @on_load :load_nif

  # code uglified to keep the post short
  def load_nif do
    nif_path = :my_app |> :code.priv_dir() |> List.to_string() |> Path.join("my_app_nif") |> String.to_charlist()
    :erlang.load_nif(nif_path, 0) # Fails with 'vendor.so' cannot open shared object file: No such file or directory
  end
end

Unfortunately :erlang.load_nif/2 doesn’t appear to have any options for specify additional library search paths. I’d be interested in hearing any suggestions on ways of getting around this? Short of just writing a run_me_not_mix_compile.sh script :slight_smile:

1 Like

Any reason why you do not set it in the makefile?

1 Like

Sorry, I probably didn’t explain quite right – the issue (as far as I can tell) isn’t in building the C code into its output artifact – it’s when I attempt to dynamically load that artifact at runtime (well, build time for the elixir code, but the C code has already been built at this point).

In the library that loads the vendor library it should just load the vendor library from the directory it already exists in, or in a hard coded relative location or so, why not just do it that way? You can get the path of the yourself (the library) and use that path to load the vendor library too. :slight_smile:

Otherwise I ‘think’ LD_LIBRARY needs to be set ‘before’ the binary runs (the binary being the BEAM since it loads ‘your’ NIF).

Or just statically build the vendor library in to yours, that’s what I always prefer.

1 Like

Can one link a SO statically?

The ‘so’ is the shared library (object), you’d want the ‘a’ file instead as that’s the static ‘archive’ that you could link to.

EDIT: There are programs that can make a static archive of an so file too, but I’ve never tried them and they occasionally have issues depending on how well programmed (or not) the so file is.

I’m definitely out of my depth here… I’d certainly prefer to statically link the vendor library, but all I have is the dynamic .so supplied by the vendor – no source code or static artifact to link against.

Your comment about the relative path put me in the right direction, I think. Linking with -Wl,-rpath="./path/to/priv" seems to work. Haven’t tested in a full release scenario with this library pulled into the main application, but I’m hopeful I can make it work. Thanks!

2 Likes

That sounds horrifying… o.O
No way to request even a statically linkable archive file?!

This worked out great for me with the same use case.