Current section
Files
Jump to
Current section
Files
lib/mix/tasks/compile.zee3.ex
defmodule Mix.Tasks.Compile.Zee3 do
use Mix.Task
@shortdoc "Downloads the pre-compiled Z3 binary for the current architecture."
# A slightly older version for improved compatibility
# with older versions of libc
@z3_version "4.13.0"
@impl Mix.Task
def run(_args) do
priv_dir = Path.join(:code.priv_dir(:zee3) || "priv", "bin")
executable_ext = if match?({:win32, _}, :os.type()), do: ".exe", else: ""
target_executable = Path.join(priv_dir, "z3" <> executable_ext)
# Only download if it doesn't already exist
unless File.exists?(target_executable) do
Mix.shell().info("Downloading Z3 #{@z3_version} binary...")
File.mkdir_p!(priv_dir)
{url, filename} = target_release()
download_and_extract(url, filename, target_executable)
Mix.shell().info("Z3 installed at #{target_executable}")
end
:ok
end
defp target_release do
arch = to_string(:erlang.system_info(:system_architecture))
os_type = :os.type()
base_url = "https://github.com/Z3Prover/z3/releases/download/z3-#{@z3_version}/"
# Map Erlang OS/Arch to Z3 GitHub Release filenames
filename =
case {os_type, arch} do
# TODO: untested
{{:unix, :darwin}, "aarch64" <> _} ->
"z3-#{@z3_version}-arm64-osx-15.7.3.zip"
# TODO: untested
{{:unix, :darwin}, _} ->
"z3-#{@z3_version}-x64-osx-15.7.3.zip"
{{:unix, :linux}, "aarch64" <> _} ->
"z3-#{@z3_version}-arm64-glibc-2.35.zip"
{{:unix, :linux}, _} ->
"z3-#{@z3_version}-x64-glibc-2.35.zip"
# TODO: untested
{{:win32, :nt}, _} ->
"z3-#{@z3_version}-x64-win.zip"
_ ->
Mix.raise(
"Unsupported architecture for automatic Z3 download: " <>
"#{os_type} - #{arch}. Please install Z3 manually."
)
end
{base_url <> filename, filename}
end
defp download_and_extract(url, zip_name, target_executable) do
# Ensure standard HTTP/SSL apps are running
Application.ensure_all_started(:inets)
Application.ensure_all_started(:ssl)
tmp_dir = System.tmp_dir!()
zip_path = Path.join(tmp_dir, zip_name)
# Download following redirects (GitHub releases redirect to S3)
{:ok, :saved_to_file} =
:httpc.request(
:get,
{String.to_charlist(url), []},
[autoredirect: true],
stream: String.to_charlist(zip_path)
)
# Extract the zip file into the temp directory
{:ok, extracted_files} =
:zip.extract(String.to_charlist(zip_path), cwd: String.to_charlist(tmp_dir))
# Find the bin/z3 executable inside the extracted folder structure
z3_path =
Enum.find(extracted_files, fn path ->
path_str = to_string(path)
String.ends_with?(path_str, "/bin/z3") or String.ends_with?(path_str, "/bin/z3.exe")
end)
unless z3_path do
Mix.raise("Could not find Z3 executable in downloaded release.")
end
# Move it to our priv/bin directory and make it executable
File.cp!(to_string(z3_path), target_executable)
File.chmod!(target_executable, 0o755)
# Clean up the downloaded zip
File.rm!(zip_path)
end
end