From 65fd6673bdb9009791190d927ffdec76b66b924c Mon Sep 17 00:00:00 2001 From: Joel Koch Date: Sat, 20 Sep 2025 20:00:42 +0200 Subject: [PATCH 1/3] support build_dot_zig --- lib/steps/patch/recompile_nifs.ex | 79 +++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 4 deletions(-) diff --git a/lib/steps/patch/recompile_nifs.ex b/lib/steps/patch/recompile_nifs.ex index 30c3f62..43241b9 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,72 @@ 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) + + {_, 0} = System.cmd("mix", ["clean", "--deps"], cd: path) + + build_result = + System.cmd("mix", ["compile"], + cd: path, + env: [{"MIX_APP_PATH", output_priv_dir}, {"MIX_TARGET", cross_target}] ++ erts_env, + stderr_to_stdout: true, + into: IO.stream() + ) + + case build_result do + {_, 0} -> + Log.info(:step, "Successfully re-built #{dep} for #{cross_target}!") + + src_priv_files = + Path.join(output_priv_dir, ["priv/**"]) |> Path.expand() |> Path.wildcard() + + final_output_priv_dir = Path.join(output_priv_dir, "priv") + + Enum.each(src_priv_files, fn file -> + file_name = Path.basename(file) + + if Path.extname(file_name) == ".so" && String.contains?(cross_target, "windows") do + new_file_name = String.replace_trailing(file_name, ".so", ".dll") + dst_fullpath = Path.join(final_output_priv_dir, new_file_name) + + Log.info(:step, "#{file} -> #{dst_fullpath}") + + File.rename!(file, dst_fullpath) + else + file_name + end + end) + + {_output, _non_zero} -> + Log.error(:step, "Failed to rebuild #{dep} for #{cross_target}!") + exit(1) + end + end + defp erts_make_env(erts_path) do ei_include = Path.join(erts_path, ["otp*/", "usr/", "include/"]) From 2ae8bd8a0c449589162fdf111004694bf4f554b5 Mon Sep 17 00:00:00 2001 From: Joel Koch Date: Sat, 20 Sep 2025 21:03:52 +0200 Subject: [PATCH 2/3] move cross target artifacts to priv/host --- lib/steps/patch/recompile_nifs.ex | 40 ++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/lib/steps/patch/recompile_nifs.ex b/lib/steps/patch/recompile_nifs.ex index 43241b9..8eb3612 100644 --- a/lib/steps/patch/recompile_nifs.ex +++ b/lib/steps/patch/recompile_nifs.ex @@ -162,7 +162,6 @@ defmodule Burrito.Steps.Patch.RecompileNIFs do |> Path.wildcard() |> List.first() - erts_env = erts_make_env(erts_path) {_, 0} = System.cmd("mix", ["clean", "--deps"], cd: path) @@ -180,23 +179,17 @@ defmodule Burrito.Steps.Patch.RecompileNIFs do Log.info(:step, "Successfully re-built #{dep} for #{cross_target}!") src_priv_files = - Path.join(output_priv_dir, ["priv/**"]) |> Path.expand() |> Path.wildcard() + Path.join([output_priv_dir, "priv", cross_target, "**"]) + |> Path.expand() + |> Path.wildcard() + |> Enum.reject(&File.dir?(&1)) final_output_priv_dir = Path.join(output_priv_dir, "priv") Enum.each(src_priv_files, fn file -> - file_name = Path.basename(file) - - if Path.extname(file_name) == ".so" && String.contains?(cross_target, "windows") do - new_file_name = String.replace_trailing(file_name, ".so", ".dll") - dst_fullpath = Path.join(final_output_priv_dir, new_file_name) - - Log.info(:step, "#{file} -> #{dst_fullpath}") - - File.rename!(file, dst_fullpath) - else - file_name - end + file + |> move_to_host_dir!(cross_target) + |> rename_to_dll_on_windows!(cross_target) end) {_output, _non_zero} -> @@ -205,6 +198,25 @@ defmodule Burrito.Steps.Patch.RecompileNIFs do end end + defp move_to_host_dir!(file, cross_target) do + dst_fullpath = String.replace(file, cross_target, "host") + + if File.exists?(dst_fullpath), do: File.rm_rf!(dst_fullpath) + File.rename!(file, dst_fullpath) + + dst_fullpath + end + + defp rename_to_dll_on_windows!(file, cross_target) do + if Path.extname(file) == ".so" && String.contains?(cross_target, "windows") 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/"]) From 6e8d8b9ef58e65e594574908e1d8677023828482 Mon Sep 17 00:00:00 2001 From: Joel Koch Date: Wed, 1 Oct 2025 20:59:59 +0200 Subject: [PATCH 3/3] recompile nif with zig build --- lib/steps/patch/recompile_nifs.ex | 47 +++++++++++++++---------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/lib/steps/patch/recompile_nifs.ex b/lib/steps/patch/recompile_nifs.ex index 8eb3612..0a5cdf7 100644 --- a/lib/steps/patch/recompile_nifs.ex +++ b/lib/steps/patch/recompile_nifs.ex @@ -146,7 +146,7 @@ defmodule Burrito.Steps.Patch.RecompileNIFs do cross_target, _extra_cflags, _extra_cxxflags, - _extra_env, + extra_env, _extra_make_args ) do dep = Atom.to_string(dep) @@ -164,12 +164,23 @@ defmodule Burrito.Steps.Patch.RecompileNIFs do erts_env = erts_make_env(erts_path) - {_, 0} = System.cmd("mix", ["clean", "--deps"], cd: 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("mix", ["compile"], + System.cmd( + "zig", + zig_args, cd: path, - env: [{"MIX_APP_PATH", output_priv_dir}, {"MIX_TARGET", cross_target}] ++ erts_env, + env: erts_env ++ extra_env, stderr_to_stdout: true, into: IO.stream() ) @@ -178,19 +189,14 @@ defmodule Burrito.Steps.Patch.RecompileNIFs do {_, 0} -> Log.info(:step, "Successfully re-built #{dep} for #{cross_target}!") - src_priv_files = - Path.join([output_priv_dir, "priv", cross_target, "**"]) + output_files = + Path.join([zig_install_dir, "**"]) |> Path.expand() |> Path.wildcard() - |> Enum.reject(&File.dir?(&1)) - final_output_priv_dir = Path.join(output_priv_dir, "priv") - - Enum.each(src_priv_files, fn file -> - file - |> move_to_host_dir!(cross_target) - |> rename_to_dll_on_windows!(cross_target) - end) + 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}!") @@ -198,17 +204,8 @@ defmodule Burrito.Steps.Patch.RecompileNIFs do end end - defp move_to_host_dir!(file, cross_target) do - dst_fullpath = String.replace(file, cross_target, "host") - - if File.exists?(dst_fullpath), do: File.rm_rf!(dst_fullpath) - File.rename!(file, dst_fullpath) - - dst_fullpath - end - - defp rename_to_dll_on_windows!(file, cross_target) do - if Path.extname(file) == ".so" && String.contains?(cross_target, "windows") do + 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}")