From a29616aa3ccdd3d6c808b96bf2374d25d521bfea Mon Sep 17 00:00:00 2001 From: rekkice Date: Tue, 6 Jan 2026 08:20:21 -0300 Subject: [PATCH 1/2] fix config for nix --- ada/.gitignore | 1 + ada/alire.toml | 23 ++++ ada/alire/alire.lock | 6 ++ ada/alire/build_hash_inputs | 8 ++ ada/alire/flags/post_fetch_done | 0 ada/alire/settings.toml | 2 + ada/build.nix | 22 +++- ada/config/erlang_nifs_config.ads | 20 ++++ ada/config/erlang_nifs_config.gpr | 28 +++++ ada/config/erlang_nifs_config.h | 20 ++++ ada/erlang_nifs.gpr | 58 ++++++++++ ada/flake.nix | 8 ++ ada/hello.gpr | 12 --- ada/src/erlang_nifs-arity_1.adb | 23 ++++ ada/src/erlang_nifs-arity_1.ads | 24 +++++ ada/src/erlang_nifs-c_apis.adb | 13 +++ ada/src/erlang_nifs-c_apis.ads | 110 +++++++++++++++++++ ada/src/erlang_nifs.adb | 167 +++++++++++++++++++++++++++++ ada/src/erlang_nifs.ads | 73 +++++++++++++ ada/src/examples.adb | 33 ++++++ ada/src/examples.ads | 5 + ada/src/examples_functions.ads | 13 +++ ada/src/hello.adb | 5 - ada/src/nif_bootstrap.cpp | 39 +++++++ ada/src/supported_erlang_types.ads | 69 ++++++++++++ 25 files changed, 760 insertions(+), 22 deletions(-) create mode 100644 ada/alire.toml create mode 100644 ada/alire/alire.lock create mode 100644 ada/alire/build_hash_inputs create mode 100644 ada/alire/flags/post_fetch_done create mode 100644 ada/alire/settings.toml create mode 100644 ada/config/erlang_nifs_config.ads create mode 100644 ada/config/erlang_nifs_config.gpr create mode 100644 ada/config/erlang_nifs_config.h create mode 100644 ada/erlang_nifs.gpr delete mode 100644 ada/hello.gpr create mode 100644 ada/src/erlang_nifs-arity_1.adb create mode 100644 ada/src/erlang_nifs-arity_1.ads create mode 100644 ada/src/erlang_nifs-c_apis.adb create mode 100644 ada/src/erlang_nifs-c_apis.ads create mode 100644 ada/src/erlang_nifs.adb create mode 100644 ada/src/erlang_nifs.ads create mode 100644 ada/src/examples.adb create mode 100644 ada/src/examples.ads create mode 100644 ada/src/examples_functions.ads delete mode 100644 ada/src/hello.adb create mode 100644 ada/src/nif_bootstrap.cpp create mode 100644 ada/src/supported_erlang_types.ads diff --git a/ada/.gitignore b/ada/.gitignore index 78deb81..868256f 100644 --- a/ada/.gitignore +++ b/ada/.gitignore @@ -1,2 +1,3 @@ obj/ result +lib/ diff --git a/ada/alire.toml b/ada/alire.toml new file mode 100644 index 0000000..6b670d7 --- /dev/null +++ b/ada/alire.toml @@ -0,0 +1,23 @@ +name = "erlang_nifs" +description = "Shiny new project" +version = "0.1.0-dev" + +authors = ["Andy Arvanitis"] +maintainers = ["Andy Arvanitis "] +maintainers-logins = ["andyarvanitis"] + +[build-switches] +"*".style_checks = "No" +"*".ada_version = "Ada2022" + +[environment] +RUNTIME_DIR.append = "dirname $(gcc --print-libgcc)" +RUNTIME_LIB.append = "/adalib/libgnat_pic.a" +ERLANG_LIB_BUNDLE.append = "erl -noshell -eval 'io:fwrite(code:root_dir())' -eval 'init:stop()'" +OPTIONS_FILE.append = "/ada_nifs_linker_options.in" + +[[actions]] +type = "pre-build" +command = [ "sh", "-c", """ DIR=$(eval ${RUNTIME_DIR}); \ + echo -L ${DIR} ${DIR}/${RUNTIME_LIB} \ + > ${TMPDIR}${OPTIONS_FILE} """ ] diff --git a/ada/alire/alire.lock b/ada/alire/alire.lock new file mode 100644 index 0000000..d08d4b6 --- /dev/null +++ b/ada/alire/alire.lock @@ -0,0 +1,6 @@ +# THIS FILE IS GENERATED. DO NOT EDIT. + +[solution] +[solution.context] +solved = true + diff --git a/ada/alire/build_hash_inputs b/ada/alire/build_hash_inputs new file mode 100644 index 0000000..a58c744 --- /dev/null +++ b/ada/alire/build_hash_inputs @@ -0,0 +1,8 @@ +environment:ERLANG_LIB_BUNDLE=erl -noshell -eval 'io:fwrite(code:root_dir())' -eval 'init:stop()' +environment:OPTIONS_FILE=/ada_nifs_linker_options.in +environment:RUNTIME_DIR=dirname $(gcc --print-libgcc) +environment:RUNTIME_LIB=/adalib/libgnat_pic.a +external:ERLANG_NIFS_LIBRARY_TYPE=default +external:LIBRARY_TYPE=default +profile:erlang_nifs=DEVELOPMENT +switches:erlang_nifs=-Og,-fdata-sections,-ffunction-sections,-g,-gnat2022,-gnatVa,-gnatW8,-gnatw.X,-gnatwa diff --git a/ada/alire/flags/post_fetch_done b/ada/alire/flags/post_fetch_done new file mode 100644 index 0000000..e69de29 diff --git a/ada/alire/settings.toml b/ada/alire/settings.toml new file mode 100644 index 0000000..acb470c --- /dev/null +++ b/ada/alire/settings.toml @@ -0,0 +1,2 @@ +last_build_profile = "erlang_nifs=DEVELOPMENT" + diff --git a/ada/build.nix b/ada/build.nix index 59bcc97..d9af700 100644 --- a/ada/build.nix +++ b/ada/build.nix @@ -1,4 +1,4 @@ -{ stdenv, gnat, gprbuild, glibc }: +{ stdenv, gnat, gprbuild, glibc, alire, erlang, git, gcc }: stdenv.mkDerivation { name = "ada-static-hello"; @@ -8,9 +8,13 @@ stdenv.mkDerivation { nativeBuildInputs = [ gprbuild gnat + alire + git + erlang ]; buildInputs = [ + glibc.dev glibc.static ]; @@ -19,6 +23,11 @@ stdenv.mkDerivation { buildPhase = '' runHook preBuild + export ERLANG_INCLUDE_DIR="-I${erlang}/lib/erlang/usr/include" + export GLIBC_INCLUDE_DIR="-I${glibc.dev}/include" + export GCC_INCLUDE_DIR="-I${gcc.cc}/include" + + # alr build gprbuild runHook postBuild @@ -27,14 +36,17 @@ stdenv.mkDerivation { installPhase = '' runHook preInstall - mkdir -p $out/bin - - # Only install what we need to run the binary. - gprinstall --prefix=$out hello.gpr \ + gprinstall --prefix=$out erlang_nifs.gpr \ --no-project \ --no-manifest \ --mode=usage + for dir in obj/Debug obj/development obj; do + if [ -f "$dir/Erlang_Nifs.so" ]; then + cp "$dir/Erlang_Nifs.so" $out + fi + done + runHook postInstall ''; } diff --git a/ada/config/erlang_nifs_config.ads b/ada/config/erlang_nifs_config.ads new file mode 100644 index 0000000..5932be0 --- /dev/null +++ b/ada/config/erlang_nifs_config.ads @@ -0,0 +1,20 @@ +-- Configuration for erlang_nifs generated by Alire +pragma Restrictions (No_Elaboration_Code); +pragma Style_Checks (Off); + +package Erlang_Nifs_Config is + pragma Pure; + + Crate_Version : constant String := "0.1.0-dev"; + Crate_Name : constant String := "erlang_nifs"; + + Alire_Host_OS : constant String := "linux"; + + Alire_Host_Arch : constant String := "x86_64"; + + Alire_Host_Distro : constant String := "distribution_unknown"; + + type Build_Profile_Kind is (release, validation, development); + Build_Profile : constant Build_Profile_Kind := development; + +end Erlang_Nifs_Config; diff --git a/ada/config/erlang_nifs_config.gpr b/ada/config/erlang_nifs_config.gpr new file mode 100644 index 0000000..a4fca91 --- /dev/null +++ b/ada/config/erlang_nifs_config.gpr @@ -0,0 +1,28 @@ +-- Configuration for erlang_nifs generated by Alire +abstract project Erlang_Nifs_Config is + Crate_Version := "0.1.0-dev"; + Crate_Name := "erlang_nifs"; + + Alire_Host_OS := "linux"; + + Alire_Host_Arch := "x86_64"; + + Alire_Host_Distro := "distribution_unknown"; + Ada_Compiler_Switches := External_As_List ("ADAFLAGS", " "); + Ada_Compiler_Switches := Ada_Compiler_Switches & + ( + "-Og" -- Optimize for debug + ,"-ffunction-sections" -- Separate ELF section for each function + ,"-fdata-sections" -- Separate ELF section for each variable + ,"-g" -- Generate debug info + ,"-gnatwa" -- Enable all warnings + ,"-gnatw.X" -- Disable warnings for No_Exception_Propagation + ,"-gnatVa" -- All validity checks + ,"-gnat2022" -- Ada 2022 Mode + ,"-gnatW8" -- UTF-8 encoding for wide characters + ); + + type Build_Profile_Kind is ("release", "validation", "development"); + Build_Profile : Build_Profile_Kind := "development"; + +end Erlang_Nifs_Config; diff --git a/ada/config/erlang_nifs_config.h b/ada/config/erlang_nifs_config.h new file mode 100644 index 0000000..de2d392 --- /dev/null +++ b/ada/config/erlang_nifs_config.h @@ -0,0 +1,20 @@ +/* Configuration for erlang_nifs generated by Alire */ +#ifndef ERLANG_NIFS_CONFIG_H +#define ERLANG_NIFS_CONFIG_H + +#define CRATE_VERSION "0.1.0-dev" +#define CRATE_NAME "erlang_nifs" + +#define ALIRE_HOST_OS "linux" + +#define ALIRE_HOST_ARCH "x86_64" + +#define ALIRE_HOST_DISTRO "distribution_unknown" + +#define BUILD_PROFILE_RELEASE 1 +#define BUILD_PROFILE_VALIDATION 2 +#define BUILD_PROFILE_DEVELOPMENT 3 + +#define BUILD_PROFILE 3 + +#endif diff --git a/ada/erlang_nifs.gpr b/ada/erlang_nifs.gpr new file mode 100644 index 0000000..7173acd --- /dev/null +++ b/ada/erlang_nifs.gpr @@ -0,0 +1,58 @@ +with "config/erlang_nifs_config.gpr"; +project Erlang_Nifs is + + -- Erlang_Include_Dir : External ("ERLANG_INCLUDE_DIR", ""); + + for Languages use ("ada", "C++"); + + for Library_Name use "Erlang_Nifs"; + for Library_Version use Project'Library_Name & ".so." & Erlang_Nifs_Config.Crate_Version; + + for Library_Interface use ("examples"); + for Library_Auto_Init use "true"; + for Library_Dir use "lib"; + + for Source_Dirs use ("src/", "config/"); + for Object_Dir use "obj/" & Erlang_Nifs_Config.Build_Profile; + for Create_Missing_Dirs use "True"; + + type Library_Type_Type is ("relocatable", "static", "static-pic"); + Library_Type : Library_Type_Type := + external ("ERLANG_NIFS_LIBRARY_TYPE", external ("LIBRARY_TYPE", "static-pic")); + for Library_Kind use Library_Type; + + package Compiler is + for Default_Switches ("Ada") use Erlang_Nifs_Config.Ada_Compiler_Switches & ("-gnata", "-gnatW8"); + for Default_Switches ("C++") use (External ("ERLANG_INCLUDE_DIR", ""), External ("GLIBC_INCLUDE_DIR", ""), External ("GCC_INCLUDE_DIR", "")); + end Compiler; + + -- for Library_Builder use "gcc -shared"; + for Archive_Builder use ("true"); -- no-op + for Archive_Indexer use ("true"); -- no-op + + Options_File := external ("OPTIONS_FILE", "unknown"); + Temp_Dir := external ("TMPDIR", "unknown"); + + case Library_Type is + when "static-pic" => + for Library_Options use ("-lc", + "-lstdc++", + -- "@" & Temp_Dir & Options_File, -- NOTE: this is from alr environment (alire.toml) + "-o" & Project'Library_Name & ".so"); + when others => + null; + end case; + + package Binder is + for Switches ("Ada") use ("-Es"); -- Symbolic traceback + end Binder; + + package Install is + for Artifacts (".") use ("share"); + end Install; + + package Clean is + for Artifacts_In_Object_Dir use Clean'Artifacts_In_Object_Dir & (Project'Library_Name & ".so"); + end Clean; + +end Erlang_Nifs; diff --git a/ada/flake.nix b/ada/flake.nix index 4ccae0d..0508883 100644 --- a/ada/flake.nix +++ b/ada/flake.nix @@ -27,10 +27,18 @@ gnat gprbuild gdb + alire + erlang + glibc.dev glibc.static + git ]; shellHook = '' echo "Available commands: gnat, gprbuild, gdb" + + export ERLANG_INCLUDE_DIR="-I${pkgs.erlang}/lib/erlang/usr/include" + export GLIBC_INCLUDE_DIR="-I${pkgs.glibc.dev}/include" + export GCC_INCLUDE_DIR="-I${pkgs.gcc.cc}/include" ''; }; }); diff --git a/ada/hello.gpr b/ada/hello.gpr deleted file mode 100644 index 6a1c74f..0000000 --- a/ada/hello.gpr +++ /dev/null @@ -1,12 +0,0 @@ - project Hello is - for Source_Dirs use ("src"); - for Object_Dir use "obj"; - for Main use ("hello.adb"); - package Binder is - for Default_Switches ("Ada") use ("-static"); - end Binder; - - package Linker is - for Default_Switches ("Ada") use ("-static"); - end Linker; -end Hello; diff --git a/ada/src/erlang_nifs-arity_1.adb b/ada/src/erlang_nifs-arity_1.adb new file mode 100644 index 0000000..9b91eb6 --- /dev/null +++ b/ada/src/erlang_nifs-arity_1.adb @@ -0,0 +1,23 @@ + +package body erlang_nifs.arity_1 is + + package args is new get_values(argument_type); use args; + package rets is new make_values(return_type); use rets; + + function nif_wrapper(env: not null access erl_nif_env_t; + argc: C.int with unreferenced; + argv: erl_nif_terms_t) + return erl_nif_term_t is + begin + return make_value(env, ada_function(get_value(env, argv(0)))); + + exception + when error: others => + return raise_erlang_exception(env, error); + + end nif_wrapper; + +begin + nif_functions.append(nif_info); + +end erlang_nifs.arity_1; diff --git a/ada/src/erlang_nifs-arity_1.ads b/ada/src/erlang_nifs-arity_1.ads new file mode 100644 index 0000000..d26a766 --- /dev/null +++ b/ada/src/erlang_nifs-arity_1.ads @@ -0,0 +1,24 @@ + +generic + erlang_name: string; + with package argument_type is new nif_supported_types (<>); + with package return_type is new nif_supported_types (<>); + with function ada_function(x: in argument_type.t) return return_type.t; +package erlang_nifs.arity_1 is + pragma elaborate_body; + pragma Assertion_Policy(Check); + +private + function nif_wrapper(env: not null access erl_nif_env_t; + argc: C.int; + argv: erl_nif_terms_t) + return erl_nif_term_t + with convention => C, + pre => argc = 1; + + nif_info : enif_func_t := (name => C.strings.new_string(erlang_name), + arity => 1, + fptr => nif_wrapper'access, + flags => 0); + +end erlang_nifs.arity_1; diff --git a/ada/src/erlang_nifs-c_apis.adb b/ada/src/erlang_nifs-c_apis.adb new file mode 100644 index 0000000..9e2af93 --- /dev/null +++ b/ada/src/erlang_nifs-c_apis.adb @@ -0,0 +1,13 @@ +package body erlang_nifs.c_apis is + + procedure populate_nif_array(arr: in out nif_funcs_array_t; + count: C.int with unreferenced) is + i : integer := 0; + begin + for nif of nif_functions loop + arr(i):= nif; + i := i + 1; + end loop; + end populate_nif_array; + +end erlang_nifs.c_apis; diff --git a/ada/src/erlang_nifs-c_apis.ads b/ada/src/erlang_nifs-c_apis.ads new file mode 100644 index 0000000..f303e6a --- /dev/null +++ b/ada/src/erlang_nifs-c_apis.ads @@ -0,0 +1,110 @@ + +private package erlang_nifs.c_apis is + + -------------------------------------------------------------------------------------- + -- Erlang NIF C APIs + -------------------------------------------------------------------------------------- + + type erl_nif_binary_t is record + size: aliased C.size_t; + data: C.strings.chars_ptr; + end record + with convention => C; + + + function enif_get_int(env: access erl_nif_env_t; + term: erl_nif_term_t; + value: in out C.int) + return integer + with convention => C, + import => true, + external_name => "enif_get_int"; + + function enif_get_double(env: access erl_nif_env_t; + term: erl_nif_term_t; + value: in out C.double) + return integer + with convention => C, + import => true, + external_name => "enif_get_double"; + + function enif_get_string_length(env: access erl_nif_env_t; + term: erl_nif_term_t; + len: in out C.unsigned; + encoding: C.unsigned) + return integer + with convention => C, + import => true, + external_name => "enif_get_string_length"; + + function enif_get_string(env: access erl_nif_env_t; + term: erl_nif_term_t; + buf: in out C.char_array; + size: C.unsigned; + encoding: C.unsigned) + return integer + with convention => C, + import => true, + external_name => "enif_get_string"; + + function enif_make_int(env: access erl_nif_env_t; + i: C.int) + return erl_nif_term_t + with convention => C, + import => true, + external_name => "enif_make_int"; + + function enif_make_double(env: access erl_nif_env_t; + d: C.double) + return erl_nif_term_t + with convention => C, + import => true, + external_name => "enif_make_double"; + + function enif_make_string(env: access erl_nif_env_t; + str: C.char_array; + encoding: C.unsigned) + return erl_nif_term_t + with convention => C, + import => true, + external_name => "enif_make_string"; + + function enif_inspect_binary(env: access erl_nif_env_t; + term: erl_nif_term_t; + value: in out erl_nif_binary_t) + return integer + with convention => C, + import => true, + external_name => "enif_inspect_binary"; + + function enif_make_new_binary(env: access erl_nif_env_t; + size: C.size_t; + term: out erl_nif_term_t) + return C.strings.chars_ptr + with convention => C, + import => true, + external_name => "enif_make_new_binary"; + + function enif_raise_exception(env: access erl_nif_env_t; + reason: erl_nif_term_t) + return erl_nif_term_t + with convention => C, + import => true, + external_name => "enif_raise_exception"; + + + -------------------------------------------------------------------------------------- + -- Function exposed to C side to fill NIF list during initialization + -------------------------------------------------------------------------------------- + + type nif_funcs_array_t is array (0 .. integer'last) of aliased enif_func_t + with convention => C; + + procedure populate_nif_array(arr: in out nif_funcs_array_t; + count: C.int) + with convention => C, + export => true, + external_name => "populate_nif_array", + pre => C.int(nif_functions.length) = count; + +end erlang_nifs.c_apis; diff --git a/ada/src/erlang_nifs.adb b/ada/src/erlang_nifs.adb new file mode 100644 index 0000000..48b9407 --- /dev/null +++ b/ada/src/erlang_nifs.adb @@ -0,0 +1,167 @@ +with interfaces; +with erlang_nifs.c_apis; use erlang_nifs.c_apis; + +package body erlang_nifs is + + use type C.unsigned; + use type C.size_t; + + ERL_NIF_LATIN1 : constant := 1; +-- ERL_NIF_UTF8 : constant := 2; + + nif_api_call_failure: exception; + + package body get_values is + + function get_int(env: not null access erl_nif_env_t; + term: erl_nif_term_t) + return integer is + i: C.int := 0; + begin + if enif_get_int(env, term, i) = 1 then + return integer(i); + else + raise nif_api_call_failure with "enif_get_int"; + end if; + end get_int; + + + function get_double(env: not null access erl_nif_env_t; + term: erl_nif_term_t) + return long_float is + d: C.double := 0.0; + begin + if enif_get_double(env, term, d) = 1 then + return long_float(d); + else + raise nif_api_call_failure with "enif_get_double"; + end if; + end get_double; + + + function get_string_length(env: not null access erl_nif_env_t; + term: erl_nif_term_t) + return C.unsigned + with post => get_string_length'result < C.unsigned'last and + C.size_t(get_string_length'result) < C.size_t'last is + len: C.unsigned := 0; + begin + if enif_get_string_length(env, term, len, ERL_NIF_LATIN1) = 0 then + raise nif_api_call_failure with "enif_get_string_length"; + end if; + return len; + end get_string_length; + + + function get_string(env: not null access erl_nif_env_t; + term: erl_nif_term_t) + return string is + -- Increment length to account for terminating null + len : constant C.unsigned := get_string_length(env, term) + 1; + buf: C.char_array(1 .. C.size_t(len)); + ret_code: integer; + begin + ret_code := enif_get_string(env, term, buf, len, ERL_NIF_LATIN1); + if ret_code > 0 then + return C.to_ada(buf); + elsif ret_code = 0 then + raise nif_api_call_failure with "enif_get_string (cannot be encoded)"; + else + raise nif_api_call_failure with "enif_get_string (buffer too small)"; + end if; + end get_string; + + function get_utf_8_string(env: not null access erl_nif_env_t; + term: erl_nif_term_t) + return string is + bin: erl_nif_binary_t; + begin + if enif_inspect_binary(env, term, bin) = 1 then + return C.strings.value(bin.data, bin.size); + else + raise nif_api_call_failure with "enif_inspect_binary returned 'false'"; + end if; + end get_utf_8_string; + + function get_value(env: not null access erl_nif_env_t; + term: erl_nif_term_t) + return value_type.t is + begin + case value_type.type_id is + when e_integer => + return value_type.to_t(get_int(env, term)); + when e_long_float => + return value_type.to_t(get_double(env, term)); + when e_string => + return value_type.to_t(get_string(env, term)); + when e_utf_8_string => + return value_type.to_t(get_utf_8_string(env, term)); + end case; + end get_value; + end get_values; + + + package body make_values is + + function make_value(env: not null access erl_nif_env_t; + value: in value_type.t) + return erl_nif_term_t is + begin + case value_type.type_id is + when e_integer => + declare + i: constant integer := value_type.from_t(value); + begin + return enif_make_int(env, C.int(i)); + end; + when e_long_float => + declare + lf: constant long_float := value_type.from_t(value); + begin + return enif_make_double(env, C.double(lf)); + end; + when e_string => + declare + s: constant string := value_type.from_t(value); + begin + return enif_make_string(env, C.to_C(s), ERL_NIF_LATIN1); + end; + when e_utf_8_string => + declare + s: constant string := value_type.from_t(value); + term: erl_nif_term_t; + data: C.strings.chars_ptr; + begin + data := enif_make_new_binary(env, s'length, term); + C.strings.update(item => data, + offset => 0, + str => s, + check => false); + return term; + end; + end case; + end make_value; + + end make_values; + + function raise_erlang_exception(env: not null access erl_nif_env_t; + message: in string) + return erl_nif_term_t is + reason: constant erl_nif_term_t := + enif_make_string(env, C.to_C(message), ERL_NIF_LATIN1); + begin + return enif_raise_exception(env, reason); + end raise_erlang_exception; + + function raise_erlang_exception(env: not null access erl_nif_env_t; + error: in exception_occurrence) + return erl_nif_term_t is + message: constant string := "Ada exception: " & + exception_name(error) & + " - " & + exception_message(error); + begin + return raise_erlang_exception(env, message); + end raise_erlang_exception; + +end erlang_nifs; diff --git a/ada/src/erlang_nifs.ads b/ada/src/erlang_nifs.ads new file mode 100644 index 0000000..a968b0d --- /dev/null +++ b/ada/src/erlang_nifs.ads @@ -0,0 +1,73 @@ +with interfaces.C; use interfaces; +with interfaces.C.strings; +use type interfaces.C.int; +with ada.containers; use ada.containers; +with ada.containers.vectors; +with ada.exceptions; use ada.exceptions; +with supported_erlang_types; use supported_erlang_types; + +package erlang_nifs is + pragma assertion_policy( check ); + + type enif_environment_t is null record; + subtype erl_nif_env_t is enif_environment_t; + type erl_nif_term_t is new interfaces.unsigned_64; + + package types renames supported_erlang_types; + + -- Package instances used directly by users creating NIF bindings + -- + package integer_type renames types.integer_type; + package long_float_type renames types.long_float_type; + package string_type renames types.string_type; + package utf_8_string_type renames types.utf_8_string_type; + +private + + generic + with package value_type is new nif_supported_types (<>); + package get_values is + function get_value(env: not null access erl_nif_env_t; + term: erl_nif_term_t) + return value_type.t; + end get_values; + + generic + with package value_type is new nif_supported_types (<>); + package make_values is + function make_value(env: not null access erl_nif_env_t; + value: in value_type.t) + return erl_nif_term_t; + end make_values; + + type erl_nif_terms_t is + array (0 .. C.int'last) of aliased erl_nif_term_t; + + type nif_fn_t is access function(env: not null access erl_nif_env_t; + argc: C.int; + argv: erl_nif_terms_t) + return erl_nif_term_t + with convention => C; + + type enif_func_t is record + name: C.strings.chars_ptr; + arity: aliased C.unsigned; + fptr: nif_fn_t; + flags: aliased C.unsigned; + end record + with Convention => C_pass_by_copy; + + package funcs_vectors is + new vectors(index_type => positive, element_type => enif_func_t); + + nif_functions: funcs_vectors.vector; + + function raise_erlang_exception(env: not null access erl_nif_env_t; + message: in string) + return erl_nif_term_t; + + function raise_erlang_exception(env: not null access erl_nif_env_t; + error: in exception_occurrence) + return erl_nif_term_t; + +end erlang_nifs; diff --git a/ada/src/examples.adb b/ada/src/examples.adb new file mode 100644 index 0000000..addbea1 --- /dev/null +++ b/ada/src/examples.adb @@ -0,0 +1,33 @@ +with erlang_nifs; use erlang_nifs; +with erlang_nifs.arity_1; +with examples_functions; use examples_functions; + +package body examples is + + package incrementer is new erlang_nifs.arity_1 + (erlang_name => "increment", + argument_type => integer_type, + return_type => integer_type, + ada_function => plus_one); + + package negater is new erlang_nifs.arity_1 + (erlang_name => "negate", + argument_type => long_float_type, + return_type => long_float_type, + ada_function => "-"); + + package uppercaser is new erlang_nifs.arity_1 + (erlang_name => "uppercase", + argument_type => string_type, + return_type => string_type, + ada_function => uppercase); + + package uppercaser_u8 is new erlang_nifs.arity_1 + (erlang_name => "uppercase_binary", + argument_type => utf_8_string_type, + return_type => utf_8_string_type, + ada_function => uppercase); + + pragma unreferenced (incrementer, negater, uppercaser, uppercaser_u8); + +end examples; diff --git a/ada/src/examples.ads b/ada/src/examples.ads new file mode 100644 index 0000000..aa43402 --- /dev/null +++ b/ada/src/examples.ads @@ -0,0 +1,5 @@ +package examples is + pragma elaborate_body; + +end examples; + diff --git a/ada/src/examples_functions.ads b/ada/src/examples_functions.ads new file mode 100644 index 0000000..f653af7 --- /dev/null +++ b/ada/src/examples_functions.ads @@ -0,0 +1,13 @@ +with ada.characters.handling; + +package examples_functions is + pragma pure; + + function plus_one(x: integer) + return integer is (x + 1) + with pre => x < integer'last; + + function uppercase(s: string) + return string is (ada.characters.handling.to_upper(s)); + +end examples_functions; diff --git a/ada/src/hello.adb b/ada/src/hello.adb deleted file mode 100644 index a9ecc95..0000000 --- a/ada/src/hello.adb +++ /dev/null @@ -1,5 +0,0 @@ -with Text_IO; use Text_IO; -procedure hello is -begin - Put_Line("Hello world!"); -end hello; diff --git a/ada/src/nif_bootstrap.cpp b/ada/src/nif_bootstrap.cpp new file mode 100644 index 0000000..b0455fe --- /dev/null +++ b/ada/src/nif_bootstrap.cpp @@ -0,0 +1,39 @@ +#include +#include + +#define NUMBER_OF_NIFS 4 + +extern "C" { + extern void Erlang_Nifsinit(); + extern void Erlang_Nifsfinal(); + extern void populate_nif_array(ErlNifFunc * funcs, int cnt); +} + +class NifsListProxy { +private: + ErlNifFunc nif_funcs[NUMBER_OF_NIFS]; // to be populated by Ada code +public: + NifsListProxy() { + assert(sizeof(NifsListProxy) == sizeof(nif_funcs)); + Erlang_Nifsinit(); + populate_nif_array(nif_funcs, NUMBER_OF_NIFS); + } + + ~NifsListProxy() { + Erlang_Nifsfinal(); + } + + ErlNifFunc operator *() { + return nif_funcs[0]; + } + + operator ErlNifFunc * () { + return &nif_funcs[0]; + } +}; + +static NifsListProxy nifsListProxy; + +extern "C" { + ERL_NIF_INIT(examples, nifsListProxy, NULL, NULL, NULL, NULL) +} diff --git a/ada/src/supported_erlang_types.ads b/ada/src/supported_erlang_types.ads new file mode 100644 index 0000000..0a24808 --- /dev/null +++ b/ada/src/supported_erlang_types.ads @@ -0,0 +1,69 @@ +with ada.strings.utf_encoding; use ada.strings.utf_encoding; + +package supported_erlang_types is + pragma pure; + + type nif_supported_type_id is + (e_integer, e_long_float, e_string, e_utf_8_string); + + type_definition_error: exception; + + generic + type t (<>) is private; + type_id: nif_supported_type_id; + with function to_t(x: in integer) return t is <>; + with function to_t(x: in long_float) return t is <>; + with function to_t(x: in string) return t is <>; + with function from_t(x: in t) return integer is <>; + with function from_t(x: in t) return long_float is <>; + with function from_t(x: in t) return string is <>; + package nif_supported_types is + -- + end nif_supported_types; + + generic + type t (<>) is private; + type t1 (<>) is private; + type t2 (<>) is private; + package conversions is + function to_t(x: in t) return t is (x); + function to_t(unused: in t) + return t1 is (raise type_definition_error); + function to_t(unused: in t) + return t2 is (raise type_definition_error); + function from_t(x: in t) return t is (x); + function from_t(unused: in t) + return t1 is (raise type_definition_error); + function from_t(unused: in t) + return t2 is (raise type_definition_error); + end conversions; + + package integer_conversions is + new conversions(integer, long_float, string); + use integer_conversions; + + package float_conversions is + new conversions(long_float, integer, string); + use float_conversions; + + package string_conversions is + new conversions(string, integer, long_float); + use string_conversions; + + -------------------------------------------------------------------------------------- + -- Package instances used directly by users creating NIF bindings + -------------------------------------------------------------------------------------- + + package integer_type is + new nif_supported_types(integer, e_integer); + + package long_float_type is + new nif_supported_types(long_float, e_long_float); + + package string_type is + new nif_supported_types(string, e_string); + + package utf_8_string_type is + new nif_supported_types(utf_8_string, e_utf_8_string); + +end supported_erlang_types; From 73a285bd247c5a6ff14b65a387eb7d2ca7987ffa Mon Sep 17 00:00:00 2001 From: rekkice Date: Tue, 6 Jan 2026 09:41:27 -0300 Subject: [PATCH 2/2] compiling but missing _nif_init --- ada/build.nix | 16 ++++++++++------ ada/src/erlang_nifs-arity_2.adb | 23 +++++++++++++++++++++++ ada/src/erlang_nifs-arity_2.ads | 24 ++++++++++++++++++++++++ ada/src/examples.adb | 11 ++++++++++- ada/src/examples_functions.ads | 4 ++++ ada/src/nif_bootstrap.cpp | 2 +- examples.ex | 25 +++++++++++++++++++++++++ 7 files changed, 97 insertions(+), 8 deletions(-) create mode 100644 ada/src/erlang_nifs-arity_2.adb create mode 100644 ada/src/erlang_nifs-arity_2.ads create mode 100644 examples.ex diff --git a/ada/build.nix b/ada/build.nix index d9af700..d6b5331 100644 --- a/ada/build.nix +++ b/ada/build.nix @@ -18,6 +18,8 @@ stdenv.mkDerivation { glibc.static ]; + NIX_LDFLAGS = "-L${glibc}/lib -L${glibc.static}/lib -L${gcc.cc.lib}/lib"; + dontConfigure = true; buildPhase = '' @@ -28,7 +30,7 @@ stdenv.mkDerivation { export GCC_INCLUDE_DIR="-I${gcc.cc}/include" # alr build - gprbuild + gprbuild -P erlang_nifs.gpr -XLIBRARY_TYPE=relocatable runHook postBuild ''; @@ -41,11 +43,13 @@ stdenv.mkDerivation { --no-manifest \ --mode=usage - for dir in obj/Debug obj/development obj; do - if [ -f "$dir/Erlang_Nifs.so" ]; then - cp "$dir/Erlang_Nifs.so" $out - fi - done + cp -r ./lib $out + + # for dir in lib; do + # if [ -f "$dir/Erlang_Nifs.so.0.1.0-dev" ]; then + # cp "$dir/Erlang_Nifs.so.0.1.0-dev" "$out/Erlang_Nifs.so" + # fi + # done runHook postInstall ''; diff --git a/ada/src/erlang_nifs-arity_2.adb b/ada/src/erlang_nifs-arity_2.adb new file mode 100644 index 0000000..35eaacc --- /dev/null +++ b/ada/src/erlang_nifs-arity_2.adb @@ -0,0 +1,23 @@ +package body erlang_nifs.arity_2 is + + package args1 is new get_values(argument_type1); use args1; + package args2 is new get_values(argument_type2); use args2; + package rets is new make_values(return_type); use rets; + + function nif_wrapper(env: not null access erl_nif_env_t; + argc: C.int with unreferenced; + argv: erl_nif_terms_t) + return erl_nif_term_t is + begin + return make_value(env, ada_function(get_value(env, argv(0)), get_value(env, argv(1)))); + + exception + when error: others => + return raise_erlang_exception(env, error); + + end nif_wrapper; + +begin + nif_functions.append(nif_info); + +end erlang_nifs.arity_2; diff --git a/ada/src/erlang_nifs-arity_2.ads b/ada/src/erlang_nifs-arity_2.ads new file mode 100644 index 0000000..2a4e7cc --- /dev/null +++ b/ada/src/erlang_nifs-arity_2.ads @@ -0,0 +1,24 @@ +generic + erlang_name: string; + with package argument_type1 is new nif_supported_types (<>); + with package argument_type2 is new nif_supported_types (<>); + with package return_type is new nif_supported_types (<>); + with function ada_function(x: in argument_type1.t; y: in argument_type2.t) return return_type.t; +package erlang_nifs.arity_2 is + pragma elaborate_body; + pragma Assertion_Policy(Check); + +private + function nif_wrapper(env: not null access erl_nif_env_t; + argc: C.int; + argv: erl_nif_terms_t) + return erl_nif_term_t + with convention => C, + pre => argc = 2; + + nif_info : enif_func_t := (name => C.strings.new_string(erlang_name), + arity => 2, + fptr => nif_wrapper'access, + flags => 0); + +end erlang_nifs.arity_2; diff --git a/ada/src/examples.adb b/ada/src/examples.adb index addbea1..9d91b05 100644 --- a/ada/src/examples.adb +++ b/ada/src/examples.adb @@ -1,5 +1,6 @@ with erlang_nifs; use erlang_nifs; with erlang_nifs.arity_1; +with erlang_nifs.arity_2; with examples_functions; use examples_functions; package body examples is @@ -28,6 +29,14 @@ package body examples is return_type => utf_8_string_type, ada_function => uppercase); - pragma unreferenced (incrementer, negater, uppercaser, uppercaser_u8); + + package adder is new erlang_nifs.arity_2 + (erlang_name => "add", + argument_type1 => integer_type, + argument_type2 => integer_type, + return_type => integer_type, + ada_function => add); + + pragma unreferenced (incrementer, negater, uppercaser, uppercaser_u8, adder); end examples; diff --git a/ada/src/examples_functions.ads b/ada/src/examples_functions.ads index f653af7..fd497c9 100644 --- a/ada/src/examples_functions.ads +++ b/ada/src/examples_functions.ads @@ -10,4 +10,8 @@ package examples_functions is function uppercase(s: string) return string is (ada.characters.handling.to_upper(s)); + function add(x: integer; y: integer) + return integer is (x + y) + with pre => x <= integer'last - y; + end examples_functions; diff --git a/ada/src/nif_bootstrap.cpp b/ada/src/nif_bootstrap.cpp index b0455fe..55ba87c 100644 --- a/ada/src/nif_bootstrap.cpp +++ b/ada/src/nif_bootstrap.cpp @@ -1,7 +1,7 @@ #include #include -#define NUMBER_OF_NIFS 4 +#define NUMBER_OF_NIFS 5 extern "C" { extern void Erlang_Nifsinit(); diff --git a/examples.ex b/examples.ex new file mode 100644 index 0000000..558c31e --- /dev/null +++ b/examples.ex @@ -0,0 +1,25 @@ +defmodule :examples do + + @on_load :load_nifs + + def load_nifs do + :erlang.load_nif('./ada/lib/libErlang_Nifs', 0) + end + + def increment(_n) do + raise "NIF increment/1 not implemented" + end + + def negate(_n) do + raise "NIF negate/1 not implemented" + end + + def uppercase(_n) do + raise "NIF uppercase/1 not implemented" + end + + def uppercase_binary(_n) do + raise "NIF uppercase_binary/1 not implemented" + end + +end