Current section
Files
Jump to
Current section
Files
lib/zig/builder.ex
defmodule Zig.Builder do
@moduledoc """
Code for interfacing with `std.build.Builder`, the interface for programmatically invoking
build code with the `zig build` command.
"""
require EEx
require Logger
build_zig_template = Path.join(__DIR__, "templates/build.zig.eex")
EEx.function_from_file(:defp, :build_zig, build_zig_template, [: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
System.get_env()
|> target_struct_from_env(zig_tree)
end
def target_struct_from_env(
%{
"TARGET_ABI" => abi,
"TARGET_ARCH" => arch,
"TARGET_OS" => os,
"TARGET_CPU" => cpu
},
_zig_tree
) do
%{abi: abi, arch: arch, os: os, cpu: cpu}
end
def target_struct_from_env(%{"CC" => cc}, _zig_tree) do
cc
|> 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
def target_struct_from_env(_, zig_tree) do
# fall back to the default zig identification
target_struct(:host, zig_tree)
end
defp to_structdef(t = %{cpu: cpu}) do
# NB: this uses zig's duck-typing facilities to only set the cpu_model field when cpu is provided.
# .explicit field is only available when it's arm; x86 will ignore this extra field.
".{.default_target = .{.cpu_arch = .#{t.arch}, .os_tag = .#{t.os}, .abi = .#{t.abi}, .cpu_model = .{ .explicit = &std.Target.arm.cpu.#{
cpu
}}}}"
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: _abi, os: os, arch: arch}) do
["lib/libc/include/#{arch}-#{os}-musl"]
end
end