rebar3 cmake 项目学习

发布时间 2023-12-22 15:45:59作者: 荣锋亮

erlang 与c 的通信方式,包含了port 以及nif,以下是使用nif 的测试

创建项目

一个lib 一个cmake

  • 命令
rebar3 new lib myapp
cd myapp
rebar3 new cmake

项目结构

├── LICENSE.md
├── README.md
├── c_src
├── Makefile
└── app.c
├── rebar.config
├── rebar.lock
└── src
    ├── myapp.app.src
    └── myapp.erl
  • nif 代码模块
    c_src/app.c
 
#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(myapp, nif_funcs, NULL, NULL, NULL, NULL);

代码上比较类似node 的napi 模块开发,与不少框架开发模式类似
src/myapp.erl 主要是调用c模块

 
-module(myapp).
 
-export([repeat/2]).
-on_load(init/0). # 加载的时候加载c 模块
 
-define(APPNAME, myapp).
-define(LIBNAME, myapp).
 
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}]}).
 
repeat(_, _) ->
    not_loaded(?LINE).

配置hooks(方便构建cmake项目)
rebar.config

 
{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"}]}.

调用

  • 启动
    直接基于rebar3 的shell 命令
 
rebar3 shell

效果

  • 调用方法
 
myapp:repeat(self(),dalong).
receive X -> X end.

效果

说明

rebar3 提供的cli 还是比较方便,可以方便的进行构建

参考资料

https://rebar3.org/docs/tutorials/building_c_cpp/
https://rebar3.org/docs/configuration/configuration/#hooks
https://www.erlang.org/doc/man/erl_nif
https://github.com/rongfengliang/rebar_cmake_erlang