Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
179 changes: 95 additions & 84 deletions lib/steps/patch/recompile_nifs.ex
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,22 @@ defmodule Burrito.Steps.Patch.RecompileNIFs do
cxxflags = Keyword.get(context.target.qualifiers, :nif_cxxflags, "")
nif_env = Keyword.get(context.target.qualifiers, :nif_env, [])
nif_make_args = Keyword.get(context.target.qualifiers, :nif_make_args, [])
skip_nifs? = Keyword.get(context.target.qualifiers, :skip_nifs, false)

if context.target.cross_build and not skip_nifs? do
triplet = Target.make_triplet(context.target)

{:local_unpacked, path: erts_location} = context.target.erts_source

nif_sniff()
|> Enum.each(fn dep ->
maybe_recompile_nif(dep, context.work_dir, erts_location, triplet, cflags, cxxflags, nif_env, nif_make_args)
end)
skip_nifs = Keyword.get(context.target.qualifiers, :skip_nifs, :all)

if context.target.cross_build do
case skip_nifs do
:all -> :do_nothing
skips when not is_list(skips) -> raise "skip_nifs must be either :all or a list"
skips ->
triplet = Target.make_triplet(context.target)
{:local_unpacked, path: erts_location} = context.target.erts_source
Enum.each(nif_sniff(), fn dep ->
maybe_recompile_nif(
dep, context.work_dir, erts_location, triplet,
cflags, cxxflags, nif_env, nif_make_args, skips
)
end)
end
end

context
Expand Down Expand Up @@ -50,84 +55,90 @@ defmodule Burrito.Steps.Patch.RecompileNIFs do
end)
end

defp maybe_recompile_nif({_, _, false}, _, _, _, _, _, _, _), do: :no_nif
defp maybe_recompile_nif({_, _, false}, _, _, _, _, _, _, _, _), do: :no_nif

defp maybe_recompile_nif(
{dep, path, true},
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 for cross-build: #{dep} -> #{cross_target}")

output_priv_dir =
Path.join(release_working_path, ["lib/#{dep}*/"])
|> Path.expand()
|> Path.wildcard()
|> List.first()

_ = System.cmd("make", ["clean"], cd: path, stderr_to_stdout: true, into: IO.stream())

# Compose env variables for cross-compilation, if we're building for linux, force dynamic linking
erts_env =
if String.contains?(cross_target, "linux") do
erts_make_env(erts_path) ++ [{"LDFLAGS", "-dynamic-linker /dev/null"}]
else
erts_make_env(erts_path)
end

# This currently is only designed for elixir_make NIFs
build_result =
System.cmd("make", ["all", "--always-make"] ++ extra_make_args,
cd: path,
stderr_to_stdout: true,
env:
[
{"MIX_APP_PATH", output_priv_dir},
{"RANLIB", "zig ranlib"},
{"AR", "zig ar"},
{"CC",
"zig cc -target #{cross_target} -O2 -dynamic -shared -Wl,-undefined=dynamic_lookup #{extra_cflags}"},
{"CXX",
"zig c++ -target #{cross_target} -O2 -dynamic -shared -Wl,-undefined=dynamic_lookup #{extra_cxxflags}"}
] ++ erts_env ++ extra_env,
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)
{dep, path, true},
release_working_path,
erts_path,
cross_target,
extra_cflags,
extra_cxxflags,
extra_env,
extra_make_args,
skip_nifs
) do
Log.info(:step, "Considering NIF #{inspect(dep)} against skip list #{inspect(skip_nifs)}...")
case Enum.member?(skip_nifs, dep) do
true -> Log.info(:step, "Ignored NIF #{inspect(dep)}.")
false ->
dep = Atom.to_string(dep)

Log.info(:step, "Going to recompile NIF for cross-build: #{dep} -> #{cross_target}")

output_priv_dir =
Path.join(release_working_path, ["lib/#{dep}*/"])
|> Path.expand()
|> Path.wildcard()
|> List.first()

_ = System.cmd("make", ["clean"], cd: path, stderr_to_stdout: true, into: IO.stream())

# Compose env variables for cross-compilation, if we're building for linux, force dynamic linking
erts_env =
if String.contains?(cross_target, "linux") do
erts_make_env(erts_path) ++ [{"LDFLAGS", "-dynamic-linker /dev/null"}]
else
file_name
erts_make_env(erts_path)
end
end)

{output, _} ->
Log.error(:step, "Failed to rebuild #{dep} for #{cross_target}!")
Log.error(:step, output)
exit(1)
# This currently is only designed for elixir_make NIFs
build_result =
System.cmd("make", ["all", "--always-make"] ++ extra_make_args,
cd: path,
stderr_to_stdout: true,
env:
[
{"MIX_APP_PATH", output_priv_dir},
{"RANLIB", "zig ranlib"},
{"AR", "zig ar"},
{"CC",
"zig cc -target #{cross_target} -O2 -dynamic -shared -Wl,-undefined=dynamic_lookup #{extra_cflags}"},
{"CXX",
"zig c++ -target #{cross_target} -O2 -dynamic -shared -Wl,-undefined=dynamic_lookup #{extra_cxxflags}"}
] ++ erts_env ++ extra_env,
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, _} ->
Log.error(:step, "Failed to rebuild #{dep} for #{cross_target}!")
Log.error(:step, output)
exit(1)
end
end
end

Expand Down
24 changes: 17 additions & 7 deletions src/maintenance.zig
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ pub fn do_maint(args: [][]u8, install_dir: []const u8) !void {
logger.warn("No sub-command provided!", .{});
} else {
if (std.mem.eql(u8, args[0], "uninstall")) {
try do_uninstall(install_dir);
const confirmed = args.len >= 2 and std.mem.eql(u8, args[1], "--no-confirm");
try do_uninstall(install_dir, confirmed);
}

if (std.mem.eql(u8, args[0], "directory")) {
Expand Down Expand Up @@ -41,12 +42,14 @@ fn confirm() !bool {
}
}

fn do_uninstall(install_dir: []const u8) !void {
logger.warn("This will uninstall the application runtime for this Burrito binary!", .{});
if ((try confirm()) == false) {
logger.warn("Uninstall was aborted!", .{});
logger.info("Quitting.", .{});
return;
fn do_uninstall(install_dir: []const u8, auto_confirmed: bool) !void {
if (!auto_confirmed) {
logger.warn("This will uninstall the application runtime for this Burrito binary!", .{});
if ((try confirm()) == false) {
logger.warn("Uninstall was aborted!", .{});
logger.info("Quitting.", .{});
return;
}
}

logger.info("Deleting directory: {s}", .{install_dir});
Expand All @@ -55,6 +58,13 @@ fn do_uninstall(install_dir: []const u8) !void {
logger.info("Quitting.", .{});
}

fn do_uninstall_confirmed(install_dir: []const u8) !void {
logger.info("Deleting directory: {s}", .{install_dir});
try std.fs.deleteTreeAbsolute(install_dir);
logger.info("Uninstall complete!", .{});
logger.info("Quitting.", .{});
}

fn print_metadata() !void {
var stdout = std.io.getStdOut().writer();
stdout.print("{s}", .{wrapper.RELEASE_METADATA_JSON}) catch {};
Expand Down