Add erlang binding (#54)
* add erlang support * add erlang support for windows * add erlang ci for linux and windows
This commit is contained in:
parent
683210d02f
commit
a362da640a
18
.github/workflows/ci.yml
vendored
18
.github/workflows/ci.yml
vendored
@ -46,6 +46,15 @@ jobs:
|
||||
cd rust
|
||||
cargo build --verbose
|
||||
cargo test --verbose
|
||||
- uses: erlef/setup-beam@v1
|
||||
with:
|
||||
otp-version: 26.2
|
||||
rebar3-version: "3.22.1"
|
||||
- name: build-test-erlang
|
||||
run: |
|
||||
cd erlang
|
||||
rebar3 compile
|
||||
rebar3 eunit
|
||||
|
||||
macos:
|
||||
runs-on: macos-latest
|
||||
@ -124,6 +133,15 @@ jobs:
|
||||
cd rust
|
||||
cargo build --verbose
|
||||
cargo test --verbose
|
||||
- uses: erlef/setup-beam@v1
|
||||
with:
|
||||
otp-version: 26.2
|
||||
rebar3-version: "3.22.1"
|
||||
- name: build-test-erlang
|
||||
run: |
|
||||
cd erlang
|
||||
rebar3 compile
|
||||
rebar3 eunit
|
||||
|
||||
qemu:
|
||||
runs-on: ubuntu-latest
|
||||
|
39
README.md
39
README.md
@ -183,6 +183,45 @@ end
|
||||
</td></tr>
|
||||
</table>
|
||||
|
||||
### ruapu with Erlang
|
||||
|
||||
<table>
|
||||
|
||||
<tr><td>
|
||||
|
||||
Compile ruapu library
|
||||
|
||||
```shell
|
||||
# from source code
|
||||
rebar3 compile
|
||||
```
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Use ruapu in Erlang `rebar3 shell`
|
||||
|
||||
```erlang
|
||||
ruapu:rua().
|
||||
{ok,["neon","vfpv4","asimdrdm","asimdhp","asimddp",
|
||||
"asimdfhm","bf16","i8mm","pmull","crc32","aes","sha1",
|
||||
"sha2","sha3","sha512","amx"]}
|
||||
> ruapu:supports("neon").
|
||||
true
|
||||
> ruapu:supports(neon).
|
||||
true
|
||||
> ruapu:supports(<<"neon">>).
|
||||
true
|
||||
> ruapu:supports("avx2").
|
||||
false
|
||||
> ruapu:supports(avx2).
|
||||
false
|
||||
> ruapu:supports(<<"avx2">>).
|
||||
false
|
||||
```
|
||||
|
||||
</td></tr>
|
||||
</table>
|
||||
|
||||
<details>
|
||||
<summary>Github-hosted runner result (Linux)</summary>
|
||||
|
||||
|
22
erlang/.gitignore
vendored
Normal file
22
erlang/.gitignore
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
.rebar3
|
||||
_*
|
||||
.eunit
|
||||
*.o
|
||||
*.beam
|
||||
*.plt
|
||||
*.swp
|
||||
*.swo
|
||||
.erlang.cookie
|
||||
ebin
|
||||
log
|
||||
erl_crash.dump
|
||||
.rebar
|
||||
logs
|
||||
_build
|
||||
.idea
|
||||
*.iml
|
||||
rebar3.crashdump
|
||||
*~
|
||||
doc/
|
||||
priv/
|
||||
_build/
|
84
erlang/CMakeLists.txt
Normal file
84
erlang/CMakeLists.txt
Normal file
@ -0,0 +1,84 @@
|
||||
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
|
||||
project(ruapu_nif C CXX)
|
||||
|
||||
if(NOT DEFINED PRIV_DIR)
|
||||
if(DEFINED MIX_APP_PATH AND NOT "${MIX_APP_PATH}" STREQUAL "")
|
||||
if(WIN32)
|
||||
string(REPLACE "\\" "/" MIX_APP_PATH "${MIX_APP_PATH}")
|
||||
endif()
|
||||
set(PRIV_DIR "${MIX_APP_PATH}/priv")
|
||||
else()
|
||||
set(PRIV_DIR "${CMAKE_CURRENT_SOURCE_DIR}/priv")
|
||||
endif()
|
||||
endif()
|
||||
message(STATUS "Using PRIV_DIR: ${PRIV_DIR}")
|
||||
|
||||
if(DEFINED ERTS_INCLUDE_DIR AND NOT "${ERTS_INCLUDE_DIR}" STREQUAL "")
|
||||
set(ERTS_INCLUDE_DIR "${ERTS_INCLUDE_DIR}")
|
||||
else()
|
||||
set(ERTS_INCLUDE_DIR_ONE_LINER "erl -noshell -eval \"io:format('~ts/erts-~ts/include/', [code:root_dir(), erlang:system_info(version)]), halt().\"")
|
||||
if(WIN32)
|
||||
execute_process(COMMAND powershell -command "${ERTS_INCLUDE_DIR_ONE_LINER}" OUTPUT_VARIABLE ERTS_INCLUDE_DIR)
|
||||
else()
|
||||
execute_process(COMMAND bash -c "${ERTS_INCLUDE_DIR_ONE_LINER}" OUTPUT_VARIABLE ERTS_INCLUDE_DIR)
|
||||
endif()
|
||||
set(ERTS_INCLUDE_DIR "${ERTS_INCLUDE_DIR}")
|
||||
endif()
|
||||
message(STATUS "Using ERTS_INCLUDE_DIR: ${ERTS_INCLUDE_DIR}")
|
||||
|
||||
if(UNIX AND APPLE)
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "-flat_namespace -undefined dynamic_lookup")
|
||||
endif()
|
||||
|
||||
if(DEFINED ENV{TARGET_GCC_FLAGS})
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} $ENV{TARGET_GCC_FLAGS}")
|
||||
endif()
|
||||
|
||||
message(STATUS "CMAKE_TOOLCHAIN_FILE: ${CMAKE_TOOLCHAIN_FILE}")
|
||||
|
||||
if(WIN32)
|
||||
string(REPLACE "\\" "/" C_SRC "${C_SRC}")
|
||||
endif()
|
||||
set(SOURCE_FILES
|
||||
"${C_SRC}/ruapu.c"
|
||||
)
|
||||
if(POLICY CMP0068)
|
||||
cmake_policy(SET CMP0068 NEW)
|
||||
endif()
|
||||
include_directories("${ERTS_INCLUDE_DIR}")
|
||||
|
||||
add_library(ruapu_nif SHARED
|
||||
${SOURCE_FILES}
|
||||
)
|
||||
install(
|
||||
TARGETS ruapu_nif
|
||||
RUNTIME DESTINATION "${PRIV_DIR}"
|
||||
)
|
||||
|
||||
set_target_properties(ruapu_nif PROPERTIES PREFIX "")
|
||||
if(NOT WIN32)
|
||||
set_target_properties(ruapu_nif PROPERTIES SUFFIX ".so")
|
||||
endif()
|
||||
set_target_properties(ruapu_nif PROPERTIES
|
||||
INSTALL_RPATH_USE_LINK_PATH TRUE
|
||||
BUILD_WITH_INSTALL_RPATH TRUE
|
||||
)
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-unused-but-set-variable -Wno-reorder")
|
||||
elseif(UNIX AND APPLE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-reorder-ctor")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "-flat_namespace -undefined dynamic_lookup")
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj /wd4996 /wd4267 /wd4068")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -g")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
|
||||
endif()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-function -Wno-sign-compare -Wno-unused-parameter -Wno-missing-field-initializers -Wno-deprecated-declarations")
|
||||
endif()
|
21
erlang/Makefile
Normal file
21
erlang/Makefile
Normal file
@ -0,0 +1,21 @@
|
||||
ifndef MIX_APP_PATH
|
||||
MIX_APP_PATH=$(shell pwd)
|
||||
endif
|
||||
|
||||
PRIV_DIR = $(MIX_APP_PATH)/priv
|
||||
RUAPU_SO = $(PRIV_DIR)/ruapu_nif.so
|
||||
C_SRC = $(shell pwd)/c_src
|
||||
|
||||
.DEFAULT_GLOBAL := build
|
||||
|
||||
build: $(RUAPU_SO)
|
||||
@echo > /dev/null
|
||||
|
||||
$(RUAPU_SO): $(C_SRC)/ruapu.c
|
||||
@ cmake -S . -B "$(PRIV_DIR)/cmake_ruapu" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DC_SRC=$(C_SRC) \
|
||||
-DMIX_APP_PATH=$(MIX_APP_PATH) \
|
||||
-DCMAKE_INSTALL_PREFIX=$(PRIV_DIR)
|
||||
@ cmake --build "$(PRIV_DIR)/cmake_ruapu" --config Release
|
||||
@ cp "$(PRIV_DIR)/cmake_ruapu/ruapu_nif.so" $(RUAPU_SO)
|
20
erlang/Makefile.win
Normal file
20
erlang/Makefile.win
Normal file
@ -0,0 +1,20 @@
|
||||
!IFNDEF MIX_APP_PATH
|
||||
MIX_APP_PATH=$(MAKEDIR)
|
||||
!ENDIF
|
||||
|
||||
PRIV_DIR = $(MIX_APP_PATH)\priv
|
||||
RUAPU_SO = $(PRIV_DIR)\ruapu_nif.dll
|
||||
C_SRC = $(MAKEDIR)\c_src
|
||||
|
||||
build: $(RUAPU_SO)
|
||||
|
||||
$(RUAPU_SO):
|
||||
@ if not exist "$(PRIV_DIR)" mkdir "$(PRIV_DIR)"
|
||||
@ cmake -G "NMake Makefiles" -B"$(PRIV_DIR)\cmake_ruapu" \
|
||||
-DC_SRC=$(C_SRC) \
|
||||
-DMIX_APP_PATH=$(MIX_APP_PATH) \
|
||||
-DCMAKE_INSTALL_PREFIX=$(PRIV_DIR)
|
||||
@ cmake --build "$(PRIV_DIR)\cmake_ruapu" --config Release
|
||||
@ cmake --install "$(PRIV_DIR)\cmake_ruapu" --config Release
|
||||
|
||||
.PHONY: build
|
135
erlang/c_src/ruapu.c
Normal file
135
erlang/c_src/ruapu.c
Normal file
@ -0,0 +1,135 @@
|
||||
#include <erl_nif.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#define RUAPU_IMPLEMENTATION
|
||||
#include "../../ruapu.h"
|
||||
|
||||
ERL_NIF_TERM atom(ErlNifEnv *env, const char *msg) {
|
||||
ERL_NIF_TERM a;
|
||||
if (enif_make_existing_atom(env, msg, &a, ERL_NIF_LATIN1)) {
|
||||
return a;
|
||||
} else {
|
||||
return enif_make_atom(env, msg);
|
||||
}
|
||||
}
|
||||
|
||||
ERL_NIF_TERM error(ErlNifEnv *env, const char *msg) {
|
||||
ERL_NIF_TERM error_atom = atom(env, "error");
|
||||
ERL_NIF_TERM msg_term = enif_make_string(env, msg, ERL_NIF_LATIN1);
|
||||
return enif_make_tuple2(env, error_atom, msg_term);
|
||||
}
|
||||
|
||||
ERL_NIF_TERM ok(ErlNifEnv *env) { return atom(env, "ok"); }
|
||||
|
||||
ERL_NIF_TERM ok_tuple(ErlNifEnv *env, ERL_NIF_TERM term) {
|
||||
return enif_make_tuple2(env, atom(env, "ok"), term);
|
||||
}
|
||||
|
||||
const char *get_string(ErlNifEnv *env, ERL_NIF_TERM term) {
|
||||
char *result = NULL;
|
||||
unsigned len;
|
||||
int ret = enif_get_list_length(env, term, &len);
|
||||
|
||||
if (ret) {
|
||||
result = (char *)enif_alloc(sizeof(char) * (len + 1));
|
||||
ret = enif_get_string(env, term, result, len + 1, ERL_NIF_LATIN1);
|
||||
if (ret > 0) {
|
||||
return result;
|
||||
} else {
|
||||
enif_free((void *)result);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ErlNifBinary bin;
|
||||
ret = enif_inspect_binary(env, term, &bin);
|
||||
if (ret) {
|
||||
result = (char *)enif_alloc(sizeof(char) * (bin.size + 1));
|
||||
if (result == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
memcpy(result, bin.data, bin.size);
|
||||
result[bin.size] = '\0';
|
||||
return result;
|
||||
}
|
||||
|
||||
ret = enif_get_atom_length(env, term, &len, ERL_NIF_LATIN1);
|
||||
if (ret) {
|
||||
result = (char *)enif_alloc(sizeof(char) * (len + 1));
|
||||
ret = enif_get_atom(env, term, result, len + 1, ERL_NIF_LATIN1);
|
||||
if (ret) {
|
||||
return result;
|
||||
} else {
|
||||
enif_free((void *)result);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ERL_NIF_TERM nif_supports(ErlNifEnv *env, int argc,
|
||||
const ERL_NIF_TERM argv[]) {
|
||||
const char *isa = get_string(env, argv[0]);
|
||||
if (isa == NULL) {
|
||||
return enif_make_badarg(env);
|
||||
}
|
||||
int supports = ruapu_supports(isa);
|
||||
enif_free((void *)isa);
|
||||
if (supports) {
|
||||
return atom(env, "true");
|
||||
} else {
|
||||
return atom(env, "false");
|
||||
}
|
||||
}
|
||||
|
||||
static ERL_NIF_TERM nif_rua(ErlNifEnv *env, int argc,
|
||||
const ERL_NIF_TERM argv[]) {
|
||||
const char *const *supported = ruapu_rua();
|
||||
const char *const *supported_start = supported;
|
||||
uint32_t count = 0;
|
||||
while (*supported_start) {
|
||||
supported_start++;
|
||||
count++;
|
||||
}
|
||||
if (count == 0) {
|
||||
ERL_NIF_TERM list = enif_make_list(env, 0, NULL);
|
||||
return ok_tuple(env, list);
|
||||
}
|
||||
|
||||
ERL_NIF_TERM *terms =
|
||||
(ERL_NIF_TERM *)enif_alloc(sizeof(ERL_NIF_TERM) * count);
|
||||
if (terms == NULL) {
|
||||
return error(env, "enif_alloc failed");
|
||||
}
|
||||
supported_start = supported;
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
terms[i] = enif_make_string(env, *supported_start, ERL_NIF_LATIN1);
|
||||
supported_start++;
|
||||
}
|
||||
|
||||
ERL_NIF_TERM list = enif_make_list_from_array(env, terms, count);
|
||||
enif_free(terms);
|
||||
return ok_tuple(env, list);
|
||||
}
|
||||
|
||||
static int on_load(ErlNifEnv *env, void **_sth1, ERL_NIF_TERM _sth2) {
|
||||
ruapu_init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_reload(ErlNifEnv *_sth0, void **_sth1, ERL_NIF_TERM _sth2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_upgrade(ErlNifEnv *_sth0, void **_sth1, void **_sth2,
|
||||
ERL_NIF_TERM _sth3) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ErlNifFunc nif_functions[] = {
|
||||
{"supports", 1, nif_supports, ERL_NIF_DIRTY_JOB_CPU_BOUND},
|
||||
{"rua", 0, nif_rua, ERL_NIF_DIRTY_JOB_CPU_BOUND},
|
||||
};
|
||||
|
||||
ERL_NIF_INIT(ruapu_nif, nif_functions, on_load, on_reload, on_upgrade, NULL);
|
18
erlang/rebar.config
Normal file
18
erlang/rebar.config
Normal file
@ -0,0 +1,18 @@
|
||||
{erl_opts, [debug_info]}.
|
||||
{deps, []}.
|
||||
|
||||
{pre_hooks,
|
||||
[{"(linux|darwin|solaris)", compile, "make"},
|
||||
{"(freebsd)", compile, "gmake"},
|
||||
{"win32", compile, "nmake /F Makefile.win"}
|
||||
]}.
|
||||
|
||||
{project_plugins, [rebar3_ex_doc]}.
|
||||
|
||||
{hex, [
|
||||
{doc, #{provider => ex_doc}}
|
||||
]}.
|
||||
|
||||
{ex_doc, [
|
||||
{source_url, "https://github.com/nihui/ruapu"}
|
||||
]}.
|
1
erlang/rebar.lock
Normal file
1
erlang/rebar.lock
Normal file
@ -0,0 +1 @@
|
||||
[].
|
14
erlang/src/ruapu.app.src
Normal file
14
erlang/src/ruapu.app.src
Normal file
@ -0,0 +1,14 @@
|
||||
{application, ruapu,
|
||||
[{description, "ruapu erlang/otp binding"},
|
||||
{vsn, "0.1.0"},
|
||||
{registered, []},
|
||||
{applications,
|
||||
[kernel,
|
||||
stdlib
|
||||
]},
|
||||
{env,[]},
|
||||
{modules, []},
|
||||
|
||||
{licenses, ["MIT"]},
|
||||
{links, []}
|
||||
]}.
|
32
erlang/src/ruapu.erl
Normal file
32
erlang/src/ruapu.erl
Normal file
@ -0,0 +1,32 @@
|
||||
-module(ruapu).
|
||||
-export([rua/0, supports/1]).
|
||||
|
||||
-ifdef(TEST).
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-endif.
|
||||
|
||||
%% @doc
|
||||
%%
|
||||
%% Get a list of supported features.
|
||||
-spec rua() -> {ok, [string()]} | {error, string()}.
|
||||
rua() ->
|
||||
ruapu_nif:rua().
|
||||
|
||||
%% @doc
|
||||
%%
|
||||
%% Check if a feature is supported.
|
||||
-spec supports(atom() | string() | binary()) -> boolean().
|
||||
supports(ISA) ->
|
||||
ruapu_nif:supports(ISA).
|
||||
|
||||
-ifdef(EUNIT).
|
||||
rua_test() ->
|
||||
?assertMatch({ok, _}, rua()).
|
||||
|
||||
supports_test() ->
|
||||
?assert(supports(<<"binary_non_exists_feature">>) =:= false),
|
||||
?assert(supports("string_non_exists_feature") =:= false),
|
||||
?assert(supports(atom_non_exists_feature) =:= false),
|
||||
{ok, Features} = rua(),
|
||||
?assert(lists:all(fun(Feature) -> supports(Feature) end, Features)).
|
||||
-endif.
|
30
erlang/src/ruapu_nif.erl
Normal file
30
erlang/src/ruapu_nif.erl
Normal file
@ -0,0 +1,30 @@
|
||||
-module(ruapu_nif).
|
||||
-export([supports/1, rua/0]).
|
||||
|
||||
-on_load(init/0).
|
||||
|
||||
-define(APPNAME, ruapu).
|
||||
-define(LIBNAME, ruapu_nif).
|
||||
|
||||
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}]}).
|
||||
|
||||
supports(_ISA) ->
|
||||
not_loaded(?LINE).
|
||||
|
||||
rua() ->
|
||||
not_loaded(?LINE).
|
Loading…
Reference in New Issue
Block a user