Current section

Files

Jump to
zigler lib zig command.ex
Raw

lib/zig/command.ex

defmodule Zig.Command do
@moduledoc """
contains all parts of the Zig library involved in calling the
zig compiler toolchain, especially with regards to the `zig` command, except
for assembling the build.zig file, which is performed by the
`Zig.Builder` module.
"""
alias Zig.{Builder, Patches}
require Logger
#############################################################################
## API
def compile(compiler, zig_tree) do
zig_executable = if compiler.module_spec.local_zig do
System.find_executable("zig")
else
# apply patches, if applicable
Patches.sync(zig_tree)
Path.join(zig_tree, "zig")
end
opts = [cd: compiler.assembly_dir, stderr_to_stdout: true]
Logger.debug("compiling nif for module #{inspect compiler.module_spec.module} in path #{compiler.assembly_dir}")
Builder.build(compiler)
case System.cmd(zig_executable, ["build"], opts) do
{_, 0} -> :ok
{err, _} ->
alias Zig.Parser.Error
Error.parse(err, compiler)
end
lib_dir = compiler.module_spec.otp_app
|> :code.lib_dir()
|> Path.join("ebin")
library_filename = Zig.nif_name(compiler.module_spec)
# copy the compiled library over to the lib/nif directory.
File.mkdir_p!(lib_dir)
compiler.assembly_dir
|> Path.join("zig-cache/lib/#{library_filename}")
|> File.cp!(Path.join(lib_dir, library_filename))
# link the compiled library to be unversioned.
symlink_filename = Path.join(lib_dir, "#{library_filename}")
unless File.exists?(symlink_filename) do
lib_dir
|> Path.join(library_filename)
|> File.ln_s!(symlink_filename)
end
:ok
end
#############################################################################
## download zig from online sources.
@doc false
def version_name(version) do
"zig-#{get_os()}-#{get_arch()}-#{version}"
end
def get_os do
case :os.type do
{:unix, :linux} ->
"linux"
{:unix, :freebsd} ->
"freebsd"
{:unix, :darwin} ->
Logger.warn("macos support is experimental")
"macos"
{_, :nt} ->
windows_warn()
"windows"
end
end
@arches %{
"i386" => "i386",
"i486" => "i386",
"i586" => "i386",
"x86_64" => "x86_64",
"armv6" => "armv6kz",
"armv7" => "armv7a",
"aarch64" => "aarch64",
"amd64" => "x86_64",
"win32" => "i386",
"win64" => "x86_64"
}
# note this is the architecture of the machine where compilation is
# being done, not the target architecture of cross-compiled
def get_arch do
arch = :system_architecture
|> :erlang.system_info()
|> List.to_string()
Enum.find_value(@arches, fn
{prefix, zig_arch} -> if String.starts_with?(arch, prefix), do: zig_arch
end) || raise arch_warn()
end
defp arch_warn, do: """
it seems like you are compiling from an unsupported architecture:
#{ :erlang.system_info(:system_architecture) }
Please leave an issue at https://github.com/ityonemo/zigler/issues
"""
defp windows_warn do
Logger.warn("""
windows is not supported, but may work.
If you find an error in the process, please leave an issue at:
https://github.com/ityonemo/zigler/issues
""")
end
@zig_dir_path Path.expand("../../zig", Path.dirname(__ENV__.file))
def fetch(version) do
zig_dir = Path.join(@zig_dir_path, version_name(version))
zig_executable = Path.join(zig_dir, "zig")
:global.set_lock({__MODULE__, self()})
unless File.exists?(zig_executable) do
# make sure the zig directory path exists and is ready.
File.mkdir_p!(@zig_dir_path)
# make sure that we're in the correct operating system.
extension = if match?({_, :nt}, :os.type()) do
".zip"
else
".tar.xz"
end
archive = version_name(version) <> extension
Logger.configure(level: :info)
Application.ensure_all_started(:mojito)
zig_download_path = Path.join(@zig_dir_path, archive)
Logger.info("downloading zig version #{version} (#{archive}) and caching in #{@zig_dir_path}.")
download_location = "https://ziglang.org/download/#{version}/#{archive}"
download_zig_archive(zig_download_path, download_location)
# untar the zig directory.
unarchive_zig(archive)
end
:global.del_lock({__MODULE__, self()})
end
def download_zig_archive(zig_download_path, download_location) do
Application.ensure_all_started(:ssl)
case Mojito.get(download_location, [], pool: false, timeout: 600_000) do
{:ok, download = %{status_code: 200}} ->
File.write!(zig_download_path, download.body)
_ -> raise "failed to download the appropriate zig archive."
end
end
def unarchive_zig(archive) do
System.cmd("tar", ["xvf", archive], cd: @zig_dir_path)
end
end