Current section
Files
Jump to
Current section
Files
lib/mix.tasks/zig.precompile.ex
defmodule Mix.Tasks.Zig.Precompile do
use Mix.Task
require Logger
@moduledoc """
precompiles a group of zig modules for all supported platforms.
In order to cross-compile windows-msvc, you will need to have the following environment
variables set:
- `MSVC_ROOT`: path to the root of a Visual Studio installation
- `WINSDK_ROOT`: path to the root of a Windows SDK installation
- `WINSDK_VERSION`: version of the Windows SDK to use, e.g. `10.0.22621.0`
TODO: add a way to specify subsets of platforms.
"""
@shortdoc "precompiles a zig module"
@msvc_dir System.get_env("MSVC_ROOT", "")
@winsdk_dir System.get_env("WINSDK_ROOT", "")
@winsdk_version System.get_env("WINSDK_VER", "")
if Enum.all?([@msvc_dir, @winsdk_dir, @winsdk_version], &(&1 != "")) or
match?({_, :nt}, :os.type()) do
@windows [:msvc, :gnu]
else
Logger.warning("not cross-compiling windows-msvc")
@windows [:gnu]
end
@triples [
aarch64: [freebsd: :none, linux: [:gnu, :musl], macos: :none],
arm: [linux: [:gnueabi, :gnueabihf, :musleabi, :musleabihf]],
x86_64: [freebsd: :none, linux: [:gnu, :musl], macos: :none, windows: @windows],
x86: [linux: [:gnu, :musl], windows: :gnu]
]
defp all_targets do
for {arch, targets} <- @triples,
{os, platforms} <- targets,
platform <- List.wrap(platforms) do
{arch, os, platform}
end
end
def run([file]) do
shas =
all_targets()
|> Enum.map(fn {arch, os, platform} -> compile(file, arch, os, platform) end)
|> inspect(pretty: true)
|> String.split("\n")
|> Enum.join("\n ")
IO.puts("""
Precompilation complete. Add the following keyword list to the shasum parameters to in web downloads,
e.g.:
```
@shasum #{shas}
use Zig, precompiled: {:web, "https://host-path-to-file/Elixir.MyModule.#VERSION.#TRIPLE.#EXT", @shasum}}
```
""")
end
defp compile(file, arch, os, platform) do
this = self()
callback = fn file -> send(this, {:result, file}) end
Application.put_env(:zigler, :precompiling, {arch, os, platform, callback})
[{module, _}] = Code.compile_file(file)
try do
receive do
{:result, file} ->
dll_sha =
file
|> File.read!()
|> then(&:crypto.hash(:sha256, &1))
|> Base.encode16(case: :lower)
if os == :windows do
pdb_file = String.replace_suffix(file, ".dll", ".pdb")
pdb_sha =
pdb_file
|> File.read!()
|> then(&:crypto.hash(:sha256, &1))
|> Base.encode16(case: :lower)
{:"#{arch}-#{os}-#{platform}", %{dll: dll_sha, pdb: pdb_sha}}
else
{:"#{arch}-#{os}-#{platform}", dll_sha}
end
end
after
# clean up the build directory
module
|> Zig.Builder.staging_directory()
|> File.rm_rf!()
Process.sleep(100)
end
end
end