Current section

Files

Jump to
rustler lib compiler.ex
Raw

lib/compiler.ex

defmodule Mix.Tasks.Compile.Rustler do
use Mix.Task
alias Rustler.Compiler.Messages
alias Rustler.Compiler.Multirust
def throw_error(error_descr) do
Mix.shell.error Messages.message(error_descr)
raise "Compilation error"
end
def run(_args) do
project = Mix.Project.config
crates = project[:rustler_crates]
if Multirust.version == :none do
throw_error(:multirust_not_installed)
end
rust_versions = Multirust.rust_versions
unless Rustler.rust_version in rust_versions do
throw_error({:rust_version_not_installed, Rustler.rust_version})
end
nif_version = :erlang.system_info(:nif_version)
unless nif_version in Rustler.nif_versions do
throw_error({:unsupported_nif_version, nif_version})
end
File.mkdir_p!(Rustler.nif_lib_dir)
compile_crates(crates)
end
def compile_crates(nil), do: throw_error(:no_crates_property)
def compile_crates(crates) do
Enum.map(crates, &compile_crate(&1))
end
def compile_crate(crate) do
cargo_data = check_crate_env(crate)
lib_name = Rustler.TomlParser.get_table_val(cargo_data, ["lib"], "name")
if lib_name == nil do
throw_error({:cargo_no_library, crate})
end
Mix.shell.info "Compiling NIF crate '#{crate}'..."
case Multirust.compile_with(crate, Rustler.rust_version, [],
IO.stream(:stdio, :line)) do
{_, 0} -> nil
_ -> raise "Compile error"
end
# TODO: Make build flavor specific
Enum.map(Path.wildcard(crate <> "/target/debug/lib#{lib_name}.*"), fn
path ->
file_name = Path.basename(path)
File.cp!(Path.absname(path), "#{Rustler.nif_lib_dir}/#{file_name}")
end)
end
def check_crate_env(crate) do
unless File.dir?(crate) do
Mix.shell.error Messages.message(:no_crates_property_message)
end
cargo_data =
case File.read("#{crate}/Cargo.toml") do
{:error, :enoent} ->
throw_error({:cargo_toml_not_found, crate})
{:ok, text} ->
Rustler.TomlParser.parse(text)
end
rustler_version = get_cargo_dep_info(cargo_data, "rustler")
rustler_codegen_version = get_cargo_dep_info(cargo_data, "rustler_codegen")
validate_versions(crate, rustler_version, rustler_codegen_version)
cargo_data
end
def validate_versions(crate, rustler, codegen)
when rustler == nil or codegen == nil do
throw_error({:no_rustler_deps, crate, Rustler.rustler_version})
end
def validate_versions(_, %{version: nil}, %{version: nil}) do
# Development, no validation
end
def validate_versions(crate, %{version: rustler_v}, %{version: codegen_v})
when rustler_v != codegen_v do
throw_error({:differing_versions, crate, rustler_v, codegen_v})
end
def validate_versions(crate, %{version: version}, %{version: version}) do
unless version == "=#{Rustler.rustler_version}" do
throw_error({:unsupported_rustler_version, crate,
"=#{Rustler.rustler_version}", version})
end
end
def get_cargo_dep_info(data, dep_name) do
table_path = ["dependencies", dep_name]
simple_key = Rustler.TomlParser.get_table_val(data, ["dependencies"], dep_name)
table = Rustler.TomlParser.get_table_vals(data, ["dependencies", dep_name])
cond do
simple_key != nil ->
%{version: simple_key}
table != nil ->
%{
version: Rustler.TomlParser.get_keys_key(table, "version"),
path: Rustler.TomlParser.get_keys_key(table, "path"),
git: Rustler.TomlParser.get_keys_key(table, "git"),
}
true ->
nil
end
end
end