【问题标题】:erlang rebar escriptize & nifserlang rebar escriptize & nifs
【发布时间】:2013-03-15 02:39:38
【问题描述】:

如果我自己编写 escript,我可以使用 nif,但是当我使用 rebar escriptize 时,找不到 nif 函数。我认为这是因为 *.so 对象没有像梁文件那样被打包。这是一个简单的例子;

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 编译 escriptize
./测试

结果是

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

有没有办法克服这个问题?

【问题讨论】:

  • 从错误看来,它是找不到的jiffy.beam,而不是*.so。也许escriptize 只考虑来自ebin 的光束,忽略依赖关系'ebins?
  • @Ed'ka,不,如果你添加一个不是 nif 的依赖项,它可以正常工作。
  • 但是如果你尝试调用jiffy:decode/1 并从priv 中删除jiffy.so 你应该得到Failed to load NIF library 错误,而不是undefined function

标签: erlang rebar erlang-nif


【解决方案1】:

问题在于erlang:load_nif/1 函数确实隐式使用任何搜索路径也不在尝试查找.so 文件时做任何聪明的事情。它只是尝试按文件名参数给出的字面意思加载文件。如果它不是绝对文件名,那么它将尝试加载相对于当前工作目录的文件。它会完全加载您告诉它加载的内容。

因此,如果您调用erlang:load_nif("jiffy.so"),它将尝试从您当前的工作目录加载"jiffy.so"。我使用的一个简单的解决方法是使用NIF_DIR 环境变量做这样的事情:

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

这可以很容易地扩展为循环搜索路径以查找文件。注意NIF_DIR不是一个特殊的名字,只是我“发明”的一个。

【讨论】:

  • 我编辑了 jiffy 代码来做到这一点,但没有帮助。这类似于@lastcanal 的建议。您无需复制到 .so 以更正位置,而是递归地查找它。我认为这个问题与加载.so无关。正如 Ed'ka 指出的那样,它说的是未定义的函数 jiffy:decode/1。
  • 是的,但从未说明应该在哪里定义 jiffy:decode/1。如果它在 .so 文件中,那么原因可能是该文件从未加载过。或者根本没有定义。
  • 它被定义为here。它会调用实际的 nif 函数,如果它被正确定义并且在代码路径中它应该给出一个 erlang:nif_error 对吗?
  • 则无法加载jiffy模块,undef错误指的是找不到jiffy模块,并不是decode/1函数未定义。
  • 是的,这是正确的。我用 jiffy 代码对 priv 目录进行了硬编码,它可以工作。如果我将 so 与存档捆绑在一起,它将不起作用。它给了我一个 {error,{load_failed,"Failed to load NIF library: 'dlopen(/Users/ali.yakamercan/src/nifes/test/jiffy/priv/jiffy.so, 2): no suitable image found. 没有find:\n\t/Users/xxx/src/nifes/test/jiffy/priv/jiffy.so: stat() failed with errno=20'"}} 因为'test' 是由 rebar 创建的 escript 而不是文件。而且,如果无法在我的 escript 中加载它们,则无法加载名称不匹配的 nif。
【解决方案2】:

似乎无法从 escript 加载 nif,因为 erlang:load_nif 不查看档案。这是因为大多数操作系统都需要可以映射到内存的*.so 的物理副本。

解决此问题的最佳方法是将 *.so 文件复制到 escript 的输出目录中。

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

查看escript builder 对应的edis。您将看到这是他们如何加载 eleveldb 的 nif 以从 escript 执行。

【讨论】:

  • edis 代码与rebar code 非常相似。我尝试使用 escript 将 jiffy.so 复制到同一目录,但没有帮助。
猜你喜欢
  • 2013-12-08
  • 2020-03-06
  • 2013-05-16
  • 2013-06-22
  • 2011-10-22
  • 2014-09-18
  • 2012-10-30
  • 2019-01-27
  • 2013-06-30
相关资源
最近更新 更多