Current section

Files

Jump to
zigler lib zig builder.ex
Raw

lib/zig/builder.ex

defmodule Zig.Builder do
@moduledoc """
Code for interfacing with `std.build.Builder`, the interface for programattically invoking
build code with the `zig build` command.
"""
require EEx
require Logger
EEx.function_from_string(:defp, :build_zig, ~S"""
const std = @import("std");
const Builder = std.build.Builder;
<% target = target_struct(@compiler_target, zig_tree) %>
const for_wasm = true;
pub fn build(b: *Builder) void {
const mode = b.standardReleaseOptions();
const target = b.standardTargetOptions(<%= to_structdef target %>);
const cflags = [_][]const u8{};
const lib = b.addSharedLibrary(
"<%= @module_spec.module %>",
"<%= @code_file %>",
.{ .versioned = .{.major = <%= @module_spec.version.major %>,
.minor = <%= @module_spec.version.minor %>,
.patch = <%= @module_spec.version.patch %>}});
lib.addSystemIncludeDir("<%= :code.root_dir %>/erts-<%= :erlang.system_info(:version) %>/include");
<%= unless @module_spec.link_libc do %>
<%= for dir <- dirs_for(target) do %>
lib.addSystemIncludeDir("<%= Path.join(zig_tree, dir) %>");
<% end %>
<% end %>
<%= for system_include_dir <- @module_spec.system_include_dirs do %>
lib.addSystemIncludeDir("<%= system_include_dir %>");
<% end %>
<%= for include_dir <- @module_spec.include_dirs do %>
lib.addIncludeDir("<%= include_dir %>");
<% end %>
lib.setBuildMode(mode);
lib.setTarget(target);
lib.single_threaded = true;
<%= if @module_spec.link_libc do %>
// use libc if it has been asked for
lib.linkSystemLibrary("c");
<% end %>
<%= for lib <- @module_spec.libs do %>
<%= cond do %>
<% String.ends_with?(lib, ".so") -> %>
lib.linkSystemLibrary("<%= Path.basename(lib) %>");
<% String.ends_with?(lib, ".dll") -> %>
lib.linkSystemLibrary("<%= Path.basename(lib) %>");
<% String.ends_with?(lib, ".dylib") -> %>
lib.linkSystemLibrary("<%= Path.basename(lib) %>");
<% String.ends_with?(lib, ".a") -> %>
lib.addObjectFile("<%= Path.basename(lib) %>");
<% true -> %>
<% raise "invalid library file" %>
<% end %>
<% end %>
// strip_symbols option?
lib.strip = <%= Mix.env() == :prod %>;
// future feature
//
// c files
// for (cfiles) |c_file| {
// lib.addCSourceFile(c_file, &cflags);
// }
lib.install();
}
""", [:assigns, :zig_tree])
def build(target, zig_tree) do
build_zig_path = Path.join(target.assembly_dir, "build.zig")
File.write!(build_zig_path, target |> Map.from_struct |> build_zig(zig_tree))
Logger.debug("wrote build.zig to #{build_zig_path}")
end
############################################################################
## cross-compilation logic.
##
## this function primarily exists to support Nerves deployments, though
## it is possible to set an arbitrary cross-compilation target using a
## setting in your `use Zig` directive. This selects the architecture
## by checking your "CC" environment variable, which is in turn set by
## Nerves, then adjusts gcc's machine type to a string which allows zig to
## select the appropriate cross-compilation settings and libc.
def target_struct(:host, zig_tree) do
{targets, 0} = zig_tree
|> Path.join("zig")
|> System.cmd(["targets"])
%{"abi" => abi, "cpu" => %{"arch" => arch}, "os" => os} = targets
|> Jason.decode!
|> Map.get("native")
%{abi: abi, arch: arch, os: os}
end
def target_struct(_other, _zig_tree) do
"CC"
|> System.get_env
|> System.cmd(~w(- -dumpmachine))
|> elem(0)
|> String.trim
|> String.split("-")
|> Enum.reject(&(&1 == "unknown"))
|> case do
[arch, os, abi] -> %{arch: arch, os: os, abi: abi}
end
end
defp to_structdef(t) do
".{.default_target = .{.cpu_arch = .#{t.arch}, .os_tag = .#{t.os}, .abi = .#{t.abi}}}"
end
defp dirs_for(target = %{os: "windows"}) do
["lib/libc/include/any-windows-any/"] ++ dirs_for_specific(target)
end
defp dirs_for(target) do
["lib/libc/musl/include"] ++ dirs_for_specific(target)
end
defp dirs_for_specific(%{abi: _, os: os, arch: "x86_64"}) do
["lib/libc/include/x86_64-#{os}-musl"]
end
defp dirs_for_specific(%{abi: "gnueabihf", os: os, arch: "arm"}) do
["lib/libc/include/arm-#{os}-gnueabihf", "lib/libc/include/arm-linux-musl"]
end
defp dirs_for_specific(%{abi: abi, os: os, arch: arch}) do
["lib/libc/include/#{arch}-#{os}-#{abi}"]
end
end