构建 C/C++
🚧
无端口编译器
在 Rebar3 中,需要使用 Makefile 或其他指令在 Rebar3 本身之外构建 C/C++ 代码。
使用 Makefile 模板
我们将从创建一个名为 test_nif
的新库开始,然后使用 test_nif
项目根目录中的 cmake
模板。
$ rebar3 new lib test_nif
===> Writing test_nif/src/test_nif.erl
===> Writing test_nif/src/test_nif.app.src
===> Writing test_nif/rebar.config
===> Writing test_nif/.gitignore
===> Writing test_nif/LICENSE
===> Writing test_nif/README.md
$ cd test_nif
$ rebar3 new cmake
===> Writing c_src/Makefile
在 test_nif
的 rebar.config
中,添加 pre_hooks 行,以便在运行 compile
时调用 make
。此外,添加 post_hooks
条目以清理构建的 C 对象文件。
rebar3 new cmake
生成的 Makefile
是一个 GNU Makefile,这意味着您需要在系统上安装 GNU Make。在示例中,我们提供了一个 FreeBSD 操作系统的处理程序,它假设 GNU Make 被称为 gmake
。
{pre_hooks,
[{"(linux|darwin|solaris)", compile, "make -C c_src"},
{"(freebsd)", compile, "gmake -C c_src"}]}.
{post_hooks,
[{"(linux|darwin|solaris)", clean, "make -C c_src clean"},
{"(freebsd)", clean, "gmake -C c_src clean"}]}.
下面是一个 NIF,它具有一个名为 repeat
的函数,该函数将接收一个 pid
和一个 Erlang 项并将其发送到该 pid
。
#include "erl_nif.h"
static ERL_NIF_TERM
mk_atom(ErlNifEnv* env, const char* atom)
{
ERL_NIF_TERM ret;
if(!enif_make_existing_atom(env, atom, &ret, ERL_NIF_LATIN1))
{
return enif_make_atom(env, atom);
}
return ret;
}
static ERL_NIF_TERM
mk_error(ErlNifEnv* env, const char* mesg)
{
return enif_make_tuple2(env, mk_atom(env, "error"), mk_atom(env, mesg));
}
static ERL_NIF_TERM
repeat(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifEnv* msg_env;
ErlNifPid pid;
ERL_NIF_TERM copy;
if(argc != 2)
{
return enif_make_badarg(env);
}
if(!enif_is_pid(env, argv[0]))
{
return mk_error(env, "not_a_pid");
}
if(!enif_get_local_pid(env, argv[0], &pid))
{
return mk_error(env, "not_a_local_pid");
}
msg_env = enif_alloc_env();
if(msg_env == NULL)
{
return mk_error(env, "environ_alloc_error");
}
copy = enif_make_copy(msg_env, argv[1]);
if(!enif_send(env, &pid, msg_env, copy))
{
enif_free(msg_env);
return mk_error(env, "error_sending_term");
}
enif_free_env(msg_env);
return mk_atom(env, "ok");
}
static ErlNifFunc nif_funcs[] = {
{"repeat", 2, repeat}
};
ERL_NIF_INIT(test_nif, nif_funcs, NULL, NULL, NULL, NULL);
修改 test_nif.erl
以从 priv
加载 test_nif
共享库并导出 repeat/2
。
-module(test_nif).
-export([repeat/2]).
-on_load(init/0).
-define(APPNAME, test_nif).
-define(LIBNAME, test_nif).
repeat(_, _) ->
not_loaded(?LINE).
init() ->
SoName = case code:priv_dir(?APPNAME) of
{error, bad_name} ->
case filelib:is_dir(filename:join(["..", priv])) of
true ->
filename:join(["..", priv, ?LIBNAME]);
_ ->
filename:join([priv, ?LIBNAME])
end;
Dir ->
filename:join(Dir, ?LIBNAME)
end,
erlang:load_nif(SoName, 0).
not_loaded(Line) ->
erlang:nif_error({not_loaded, [{module, ?MODULE}, {line, Line}]}).
请注意,error()
函数将导致 Dializer 错误。使用 erlang:nif_error/1
不会导致错误,并且在此处更佳。
运行 rebar3 shell
并尝试一下 NIF。
$ rebar3 shell
===> Verifying dependencies...
===> Compiling test_nif
Erlang/OTP 17 [erts-6.3] [source] [64-bit] [smp:4:4] [async-threads:0] [kernel-poll:false]
Eshell V6.3 (abort with ^G)
1> test_nif:repeat(self(), hello).
ok
2> receive X -> X end.
hello
参考
上次修改时间:2023年9月11日:修复 test_nif 示例中的编译器警告 (f113813)