Current section

Files

Jump to
zigler lib mix.tasks zig.precompile.ex
Raw

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]
]
def run([file]) do
shas =
for {arch, targets} <- @triples, reduce: [] do
acc ->
for {os, platforms} <- targets, reduce: acc do
acc2 ->
for platform <- List.wrap(platforms), reduce: acc2 do
acc3 -> [compile(file, arch, os, platform) | acc3]
end
end
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} ->
file
|> File.read!()
|> then(&:crypto.hash(:sha256, &1))
|> Base.encode16(case: :lower)
|> then(&{:"#{arch}-#{os}-#{platform}", &1})
end
after
# clean up the build directory
module
|> Zig.Builder.staging_directory()
|> File.rm_rf!()
Process.sleep(100)
end
end
end