defmodule Zig.Module do @moduledoc """ abstraction representing multiple zig nif functions bound into a single module """ # TODO: write out a struct that will hold the nif options for the module. alias Zig.Nif alias Zig.Resources import Zig.QuoteErl require EEx nif = Path.join(__DIR__, "templates/module.zig.eex") EEx.function_from_file(:def, :module_file, nif, [:assigns]) def render_zig(nifs, resources, callbacks, module) do resources = append_concurrency_resources(resources, nifs) module_file(binding()) end defp append_concurrency_resources(resources, nifs) do nifs |> Enum.flat_map(& &1.concurrency.resources(&1)) |> Kernel.++(resources) end # internal helpers defp table_entries(nifs) when is_list(nifs) do nifs |> Enum.flat_map(&Nif.table_entries/1) |> Enum.join(",") end @index_of %{major: 0, minor: 1} defp nif_version(at) do :nif_version |> :erlang.system_info() |> List.to_string() |> String.split(".") |> Enum.at(@index_of[at]) end # CODE RENDERING def render_elixir(code, function_code, module, manifest, opts) do nif_name = "#{module}" load_nif_fn = case function_code do [] -> logger_msg = "module #{inspect(module)} does not compile its nifs" quote do def __load_nifs__ do require Logger Logger.info(unquote(logger_msg)) end end _ -> quote do def __load_nifs__ do # LOADS the nifs from :code.lib_dir() <> "ebin", which is # a path that has files correctly moved in to release packages. require Logger unquote(opts[:otp_app]) |> :code.priv_dir() |> Path.join("lib") |> Path.join(unquote(nif_name)) |> String.to_charlist() |> :erlang.load_nif(0) |> case do :ok -> Logger.debug("loaded module at #{unquote(nif_name)}") error = {:error, any} -> Logger.error("loading module #{unquote(nif_name)} #{inspect(any)}") end end end end quote do @zig_code unquote(code) unquote_splicing(function_code) unquote(load_nif_fn) require Zig.Manifest Zig.Manifest.resolver(unquote(manifest)) def _format_error(_, [{_, _, _, opts} | _rest] = _stacktrace) do if formatted = opts[:zigler_error], do: formatted, else: %{} end end end def render_erlang(_code, function_code, module, _manifest, opts) do otp_app = Keyword.fetch!(opts, :otp_app) module_name = Atom.to_charlist(module) init_function = quote_erl( """ '__init__'() -> erlang:load_nif(filename:join(code:priv_dir(unquote(otp_app)), unquote(module_id)), []). """, otp_app: otp_app, module_id: ~C'lib/' ++ module_name ) Enum.flat_map(function_code, & &1) ++ init_function end end