diff --git a/lib/steps/patch/recompile_nifs.ex b/lib/steps/patch/recompile_nifs.ex index 30c3f62..0a5cdf7 100644 --- a/lib/steps/patch/recompile_nifs.ex +++ b/lib/steps/patch/recompile_nifs.ex @@ -37,6 +37,7 @@ defmodule Burrito.Steps.Patch.RecompileNIFs do context end + @compilers [:elixir_make, :build_dot_zig] def nif_sniff() do # The current procedure for finding out if a dependency has a NIF: # - List all deps in the project using Mix.Project.deps_paths/0 @@ -51,18 +52,22 @@ defmodule Burrito.Steps.Patch.RecompileNIFs do Enum.map(paths, fn {dep_name, path} -> Mix.Project.in_project(dep_name, path, fn module -> if module && Keyword.has_key?(module.project(), :compilers) do - {dep_name, path, Enum.member?(module.project()[:compilers], :elixir_make)} + {dep_name, path, first_matching_compiler(module, @compilers)} else - {dep_name, path, false} + {dep_name, path, nil} end end) end) end - defp maybe_recompile_nif({_, _, false}, _, _, _, _, _, _, _), do: :no_nif + defp first_matching_compiler(module, compilers) do + Enum.find(module.project()[:compilers], &(&1 in compilers)) + end + + defp maybe_recompile_nif({_, _, nil}, _, _, _, _, _, _, _), do: :no_nif defp maybe_recompile_nif( - {dep, path, true}, + {dep, path, :elixir_make}, release_working_path, erts_path, cross_target, @@ -134,6 +139,81 @@ defmodule Burrito.Steps.Patch.RecompileNIFs do end end + defp maybe_recompile_nif( + {dep, path, :build_dot_zig}, + release_working_path, + erts_path, + cross_target, + _extra_cflags, + _extra_cxxflags, + extra_env, + _extra_make_args + ) do + dep = Atom.to_string(dep) + + Log.info( + :step, + "Going to recompile NIF with build_dot_zig for cross-build: #{dep} -> #{cross_target}" + ) + + output_priv_dir = + Path.join(release_working_path, ["lib/#{dep}*/"]) + |> Path.expand() + |> Path.wildcard() + |> List.first() + + erts_env = erts_make_env(erts_path) + + build_mode = "ReleaseSafe" + zig_install_dir = Path.join([output_priv_dir, "priv", "host"]) + + zig_args = [ + "build", + "-Dtarget=#{cross_target}", + "-Doptimize=#{build_mode}", + "--prefix", + "#{zig_install_dir}" + ] + + build_result = + System.cmd( + "zig", + zig_args, + cd: path, + env: erts_env ++ extra_env, + stderr_to_stdout: true, + into: IO.stream() + ) + + case build_result do + {_, 0} -> + Log.info(:step, "Successfully re-built #{dep} for #{cross_target}!") + + output_files = + Path.join([zig_install_dir, "**"]) + |> Path.expand() + |> Path.wildcard() + + if String.contains?(cross_target, "windows") do + Enum.each(output_files, &rename_to_dll!(&1)) + end + + {_output, _non_zero} -> + Log.error(:step, "Failed to rebuild #{dep} for #{cross_target}!") + exit(1) + end + end + + defp rename_to_dll!(file) do + if Path.extname(file) == ".so" do + dst_fullpath = String.replace_trailing(file, ".so", ".dll") + + Log.info(:step, "#{file} -> #{dst_fullpath}") + + File.rename!(file, dst_fullpath) + end + end + defp erts_make_env(erts_path) do ei_include = Path.join(erts_path, ["otp*/", "usr/", "include/"])