Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

erlang rebar escriptize & nifs

I can use nif's if I write the escript myself, however when I use rebar escriptize the nif functions cannot be found. I think it is because *.so objects are not getting packed like beam files. Here is an simple example;

rebar.config:

{deps, [
   {'jiffy', "", {git, "https://github.com/davisp/jiffy.git", {branch, master}}}
]}.
{escript_incl_apps, [jiffy]}.
%% I tried this to see what happens if the so got in there but didn't help
{escript_incl_extra, [{"deps/jiffy/priv/jiffy.so", "/path/to/my/proj"}]}.

test.erl:

-module(test).

-export([main/1]).

main(_Args) ->
    jiffy:decode(<<"1">>),
    ok.

rebar get-deps compile escriptize
./test

and the result is

escript: exception error: undefined function jiffy:decode/1
  in function  test:main/1 (src/test.erl, line 7)
  in call from escript:run/2 (escript.erl, line 741)
  in call from escript:start/1 (escript.erl, line 277)
  in call from init:start_it/1
  in call from init:start_em/1

Is there a way to overcome this ?

like image 913
cashmere Avatar asked Mar 25 '13 14:03

cashmere


People also ask

What is rebar Erlang?

rebar is an Erlang build tool that makes it easy to compile and test Erlang applications, port drivers and releases. rebar is a self-contained Erlang script, so it's easy to distribute or even embed directly in a project.

Where is rebar config?

the rebar. config file at the application root. each top-level app's (in apps/ or libs/ ) rebar.


2 Answers

The problem is that the erlang:load_nif/1 function does not implicitly use any search path nor do anything smart in trying to find the .so file. It just tries to load the file literally as given by the file name argument. If it is not an absolute file name then it will try to load the file relative to the current working directory. It loads exactly what you tell it to load.

So if you call erlang:load_nif("jiffy.so") then it will try to load "jiffy.so" from your current working directory. A simple work around that I have used is to do something like this which uses the NIF_DIR environment variable:

load_nifs() ->
    case os:getenv("NIF_DIR") of
        false -> Path = ".";
        Path -> Path
    end,
    ok = erlang:load_nif(Path ++ "/gpio_nifs", 0).

This can easily be extended to loop down a search path to find the file. Note that NIF_DIR is not a special name, just one I have "invented".

like image 96
rvirding Avatar answered Oct 20 '22 14:10

rvirding


It does not seem possible to load a nif from an escript because erlang:load_nif does not look into archives. This is because most operating systems require a physical copy of the *.so that can be mapped to memory.

The best way to overcome this is by copying the *.so files into the output directory of the escript.

  {ok, _Bytes} = file:copy("deps/jiffy/priv/jiffy.so", "bin/jiffy.so"),

Take a look at the escript builder for edis. You will see this is how they load the eleveldb's nif for execution from an escript.

like image 29
lastcanal Avatar answered Oct 20 '22 15:10

lastcanal