Skip to content

Add example project #3

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Mar 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
with:
otp-version: "27.2"
elixir-version: "1.18.2"
- run: mix deps.get
- run: mix deps.get --check-locked
- run: mix deps.compile
- run: mix format --check-formatted
- run: mix deps.unlock --check-unused
Expand Down Expand Up @@ -59,3 +59,10 @@ jobs:
working-directory: test
- run: mix test
working-directory: test
- name: Test example/
run: |
mix deps.get --check-locked
mix format --check-formatted
mix deps.unlock --check-unused
mix test --warnings-as-errors
working-directory: example
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ FINE_NIF(add, 0);
FINE_INIT("Elixir.MyLib.NIF");
```

See [`example/`](https://github.com/elixir-nx/fine/tree/main/example) project.

## Encoding/Decoding

Terms are automatically encoded and decoded at the NIF boundary based
Expand Down
4 changes: 4 additions & 0 deletions example/.formatter.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Used by "mix format"
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
]
23 changes: 23 additions & 0 deletions example/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# The directory Mix will write compiled artifacts to.
/_build/

# If you run "mix test --cover", coverage assets end up here.
/cover/

# The directory Mix downloads your dependencies sources to.
/deps/

# Where third-party dependencies like ExDoc output generated docs.
/doc/

# If the VM crashes, it generates a dump, let's ignore it too.
erl_crash.dump

# Also ignore archive artifacts (built via "mix archive.build").
*.ez

# Ignore package tarball (built via "mix hex.build").
example-*.tar

# Temporary files, for example, from tests.
/tmp/
29 changes: 29 additions & 0 deletions example/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
PRIV_DIR := $(MIX_APP_PATH)/priv
NIF_PATH := $(PRIV_DIR)/libexample.so
C_SRC := $(shell pwd)/c_src

CPPFLAGS := -shared -fPIC -std=c++17 -Wall -Wextra
CPPFLAGS += -I$(ERTS_INCLUDE_DIR) -I$(FINE_INCLUDE_DIR)

ifdef DEBUG
CPPFLAGS += -g
else
CPPFLAGS += -O3
endif

ifndef TARGET_ABI
TARGET_ABI := $(shell uname -s | tr '[:upper:]' '[:lower:]')
endif

ifeq ($(TARGET_ABI),darwin)
CPPFLAGS += -undefined dynamic_lookup -flat_namespace
endif

SOURCES := $(wildcard $(C_SRC)/*.cpp)

all: $(NIF_PATH)
@ echo > /dev/null # Dummy command to avoid the default output "Nothing to be done"

$(NIF_PATH): $(SOURCES)
@ mkdir -p $(PRIV_DIR)
$(CXX) $(CPPFLAGS) $(SOURCES) -o $(NIF_PATH)
14 changes: 14 additions & 0 deletions example/Makefile.win
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
PRIV_DIR=$(MIX_APP_PATH)\priv
NIF_PATH=$(PRIV_DIR)\libexample.dll

C_SRC=$(MAKEDIR)\c_src
CPPFLAGS=/LD /std:c++17 /W4 /O2 /EHsc
CPPFLAGS=$(CPPFLAGS) /I"$(ERTS_INCLUDE_DIR)" /I"$(FINE_INCLUDE_DIR)"

SOURCES=$(C_SRC)\*.cpp

all: $(NIF_PATH)

$(NIF_PATH): $(SOURCES)
@ if not exist "$(PRIV_DIR)" mkdir "$(PRIV_DIR)"
cl $(CPPFLAGS) $(SOURCES) /Fe"$(NIF_PATH)"
10 changes: 10 additions & 0 deletions example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Example

Example project using Fine to implement NIFs.

To run tests, execute:

```shell
$ mix deps.get
$ mix test
```
8 changes: 8 additions & 0 deletions example/c_src/example.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#include <fine.hpp>

int64_t add(ErlNifEnv *env, int64_t x, int64_t y) {
return x + y;
}

FINE_NIF(add, 0);
FINE_INIT("Elixir.Example");
24 changes: 24 additions & 0 deletions example/lib/example.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
defmodule Example do
@on_load :__on_load__

def __on_load__ do
path = :filename.join(:code.priv_dir(:example), ~c"libexample")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this can also be the following, wdyt?

Suggested change
path = :filename.join(:code.priv_dir(:example), ~c"libexample")
path = Application.app_dir(:example, ["priv", "libexample"])

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, but according to erlang:load_nif/2 typespec the path should be a charlist, so probably safer to stick to that.


case :erlang.load_nif(path, 0) do
:ok -> :ok
{:error, reason} -> raise "failed to load NIF library, reason: #{inspect(reason)}"
end
end

@doc """
Adds two numbers using NIF.

## Examples

iex> Example.add(1, 2)
3
"""
def add(_x, _y) do
:erlang.nif_error("nif not loaded")
end
end
27 changes: 27 additions & 0 deletions example/mix.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
defmodule Example.MixProject do
use Mix.Project

def project do
[
app: :example,
version: "0.1.0",
elixir: "~> 1.15",
compilers: [:elixir_make] ++ Mix.compilers(),
make_env: fn -> %{"FINE_INCLUDE_DIR" => Fine.include_dir()} end,
deps: deps()
]
end

def application do
[
extra_applications: [:logger]
]
end

defp deps do
[
{:elixir_make, "~> 0.9.0"},
{:fine, path: ".."}
]
end
end
3 changes: 3 additions & 0 deletions example/mix.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
%{
"elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"},
}
4 changes: 4 additions & 0 deletions example/test/example_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
defmodule ExampleTest do
use ExUnit.Case, async: true
doctest Example
end
1 change: 1 addition & 0 deletions example/test/test_helper.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ExUnit.start()