9
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ElixirAdvent Calendar 2024

Day 11

ExTaskを使ったNIFの作り方

Last updated at Posted at 2024-11-30

ExTaskを使ってNIFを作るサンプルプログラムを作りました.

TaskはGoでできたプラットフォーム非依存の新しいタスクランナーです.

ExTaskは私が作ったElixirのOSSで,elixir_makeみたいに,ElixirからTaskを呼び出せるようにします.

たとえば次のようなCコードをNIFとして作りたいとします.

lib/sample_ex_task.ex
defmodule SampleExTask do
  @moduledoc """
  Documentation for `SampleExTask`.
  """
  require Logger

  @on_load :load_nif

  @doc false
  def load_nif do
    nif_file = ~c'#{Application.app_dir(:sample_ex_task, "priv/libnif")}'

    case :erlang.load_nif(nif_file, 0) do
      :ok -> :ok
      {:error, {:reload, _}} -> :ok
      {:error, reason} -> Logger.error("Failed to load NIF: #{inspect(reason)}")
    end
  end

  @doc """
  ok.

  ## Examples

      iex> SampleExTask.ok()
      :ok

  """
  def ok(), do: :erlang.nif_error(:not_loaded)
end
nif_src/lib_nif.c
#include <erl_nif.h>

static ERL_NIF_TERM ok(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
    return enif_make_atom(env, "ok");
}

static ErlNifFunc nif_funcs [] =
{
    {"ok", 0, ok}
};

ERL_NIF_INIT(Elixir.SampleExTask, nif_funcs, NULL, NULL, NULL, NULL)

ごくシンプルに:okを返すだけのNIFコードです.

      iex> SampleExTask.ok()
      :ok

mix.exsを次のようにします.

mix.exs
defmodule SampleExTask.MixProject do
  use Mix.Project

  def project do
    [
      app: :sample_ex_task,
      version: "0.1.0",
      elixir: "~> 1.17",
      start_permanent: Mix.env() == :prod,
      deps: deps(),
      compilers: [:ex_task] ++ Mix.compilers
    ]
  end

  # Run "mix help compile.app" to learn about applications.
  def application do
    [
      extra_applications: [:logger]
    ]
  end

  # Run "mix help deps" to learn about dependencies.
  defp deps do
    [
      # {:dep_from_hexpm, "~> 0.3.0"},
      # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
      {:ex_task, "~> 0.1", runtime: false}
    ]
  end
end

ポイントは2つ.

  • projectcompilers: [:ex_task] ++ Mix.compilersを足す
  • deps{:ex_task, "~> 0.1", runtime: false}を足す

次のようなTaskfile.ymlを配置します.

Taskfile.yaml
version: '3'

tasks:
  default:
    vars:
      PRIV: $MIX_APP_PATH/priv
      BUILD: $MIX_APP_PATH/obj
      NIF: "{{.PRIV}}/libnif.so"
      ERLANG_PATH:
        sh: elixir --eval ':code.root_dir |> to_string() |> IO.puts'
      ERL_EI_INCLUDE_DIR: 
        sh: echo "${ERL_EI_INCLUDE_DIR:-{{.ERLANG_PATH}}/usr/include}"
      ERL_EI_LIBDIR:
        sh: echo "${ERL_EI_LIBDIR:-{{.ERLANG_PATH}}/usr/lib}"
      CFLAGS_O:
        sh: |
          if [ -z $CROSSCOMPILE ]; then
          if [ $(uname -s) -ne 'Darwin' ]; then
          echo -fPIC
          else
          :
          fi
          else
          echo -fPIC
          fi
      LDFLAGS_O:
        sh: |
          if [ -z $CROSSCOMPILE ]; then
          if [ $(uname -s) -ne 'Darwin' ]; then
          echo -fPIC -shared
          else
          echo -undefined dynamic_lookup -dynamiclib
          fi
          else
          echo -fPIC -shared
          fi
      CFLAGS: >-
        -I{{.ERL_EI_INCLUDE_DIR}}
        $CFLAGS
        {{.CFLAGS_O}}
        -std=c11
        -O3
        -Wall
        -Wextra
        -Wno-unused-function
        -Wno-unused-parameter
        -Wno-missing-field-initializers
      LDFLAGS: >-
        -L{{.ERL_EI_LIBDIR}}
        $LDFLAGS
        {{.LDFLAGS_O}}
      NIF_SRC_DIR: nif_src
      C_SRC: $NIF_SRC_DIR/libnif.c
    preconditions:
      - sh: '[ -n {{.ERLANG_PATH}} ]'
        msg: Could not find the Elixir installation. Check to see that 'elixir'.
    cmds:
      - mkdir -p {{.PRIV}}
      - mkdir -p {{.BUILD}}
      - cc -c {{.CFLAGS}} -o {{.BUILD}}/libnif.o {{.NIF_SRC_DIR}}/libnif.c
      - cc -o {{.NIF}} {{.LDFLAGS}} {{.BUILD}}/libnif.o
    status:
      - test -f {{.NIF}}
    silent: true

条件分岐などで,シェルの力を適宜借りないといけないのが,少し難点ですね.

9
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?