rebar3_otter
A rebar3 plugin that builds Rust NIF crates written with
otter-nif and installs the resulting
shared library where erlang:load_nif/2 expects it. Write NIFs in Rust, then
let this plugin drive cargo from your normal rebar3 build.
The plugin has no Rust dependency of its own — it treats the Rust toolchain as
an external tool, the same way rebar3 treats the Erlang compiler.
Requirements
- OTP 27 — the plugin uses OTP-27 doc syntax (
-moduledoc/-doc). cargoonPATH— toolchain installation is the user's responsibility.
(The NIF runtime floor is lower — NIF 2.17 / OTP 26 — but that governs loading the compiled library into a BEAM, not this build-time plugin.)
Install
Add the plugin to your rebar.config:
{plugins, [rebar3_otter]}.
Declare the crates to build:
{otter_crates, [
#{name => "my_nif", path => "native/my_nif"}
]}.
Each entry in otter_crates is a map describing one crate:
| Key | Required? | Type | Description |
|---|---|---|---|
name | Required | string() | binary() | Cargo crate name — must match [package].name in the crate's Cargo.toml. |
path | Required | string() | binary() | Crate directory, relative to the declaring app's directory. |
mode | Optional | release | debug | Cargo build profile. Defaults to release. |
features | Optional | [string() | binary()] | Cargo features to enable. Defaults to []. |
target | Optional | string() | binary() | undefined | Cross-compilation target triple; undefined builds for the host. Defaults to undefined. |
String-typed values (name, path, target, and each features element)
accept a string or a binary, normalized to a string. They mirror Cargo's own
strings and carry hyphens (e.g. "x86_64-unknown-linux-gnu") naturally.
Then wire the providers as build hooks:
{provider_hooks, [
{pre, [
{compile, otter_compile},
{clean, otter_clean}
]}
]}.
otter_crates is read per application: path resolves relative to that
app's directory and the built .so is installed into that app's
priv/native/. In an umbrella project each app declares the crates it owns.
Scaffold a crate
rebar3 otter new --name my_nif
Generates native/my_nif/ with a minimal Cargo.toml (depending on
otter-nif) and a src/lib.rs containing a hello NIF. The Erlang loader is
intentionally not generated — NIF loading is two lines of standard Erlang you
should write yourself:
-module(my_nif).
-on_load(init/0).
init() ->
erlang:load_nif(filename:join(code:priv_dir(my_app), "native/my_nif"), 0).
%% Stub replaced at load time by the NIF implementation
hello() -> exit(nif_not_loaded).
Providers
| Provider | Namespace | Role |
|---|---|---|
otter_compile | default | pre_compile hook — runs cargo build, copies the cdylib into priv/native/. |
otter_clean | default | pre_clean hook — removes the installed .so and the crate's target/. |
new | otter | rebar3 otter new --name <n> — scaffolds a NIF crate. |
cargo runs with ERTS_INCLUDE_DIR set to the running ERTS's include dir, so
the native build can locate erl_nif.h without extra configuration.
See DESIGN.md for the full provider design, artifact-path resolution, and error handling.
License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.