Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 122 additions & 0 deletions lib/mix/tasks/burrito.dinein.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
defmodule Mix.Tasks.Burrito.Dinein do
@shortdoc "Builds a local Burrito lc binary for the current host platform"

@moduledoc """
#{@shortdoc}.

mix burrito.dinein [--target TARGET]

Detects the current host platform, builds `app/`'s Burrito binary for that
single target only (`MIX_ENV=prod BURRITO_TARGET=<target> mix release lc
--overwrite`), then installs it as `app/burrito_out/lc` (dropping the
target suffix) so it's immediately runnable as `./app/burrito_out/lc`.

Supported targets (auto-detected from the current host):
- `macos_aarch64` — macOS Apple Silicon
- `linux_x86_64` — Linux x86_64
- `windows_x86_64` — Windows x86_64

Pass `--target TARGET` (`-t`) to override detection — useful for explicit
cross-compilation when Zig can produce the target from this host.
"""

use Mix.Task

alias RepoTasks.Shell

@supported_targets ~w[macos_aarch64 linux_x86_64 windows_x86_64]

@impl Mix.Task
def run(argv) do
{opts, args} =
OptionParser.parse!(argv, strict: [target: :string], aliases: [t: :target])

unless args == [] do
Mix.raise("Usage: mix burrito.dinein [--target TARGET]")
end

target = opts[:target] || detect_target!()

Mix.shell().info("==> Fetching app/ deps")
Shell.run!("mix", ["deps.get"], cd: "app")

Mix.shell().info("==> Building Burrito binary for #{target}")

Shell.run!(
"mix",
["release", "lc", "--overwrite"],
cd: "app",
env: [{"MIX_ENV", "prod"}, {"BURRITO_TARGET", target}]
)

# Burrito names output lc_<target> (lc_<target>.exe on Windows).
# Rename to plain lc (lc.exe) so it's immediately usable without knowing
# the target suffix.
ext = if target == "windows_x86_64", do: ".exe", else: ""
src = Path.join("app/burrito_out", "lc_#{target}#{ext}")
dst = Path.join("app/burrito_out", "lc#{ext}")

case File.rename(src, dst) do
:ok ->
Mix.shell().info("==> Built: #{dst}")

{:error, reason} ->
Mix.raise("Failed to install #{src} as #{dst}: #{:file.format_error(reason)}")
end

:ok
end

@doc """
Returns the Burrito target name matching the current host platform.

Raises `Mix.Error` with an actionable message on unsupported platforms.
"""
@spec detect_target!() :: String.t()
def detect_target! do
os = detect_os()
cpu = detect_cpu()

case {os, cpu} do
{:darwin, :aarch64} ->
"macos_aarch64"

{:linux, :x86_64} ->
"linux_x86_64"

{:windows, :x86_64} ->
"windows_x86_64"

{os, cpu} ->
Mix.raise(
"Unsupported platform: #{os}/#{cpu}. " <>
"Supported targets: #{Enum.join(@supported_targets, ", ")}"
)
end
end

defp detect_os do
case :os.type() do
{:win32, _} -> :windows
{:unix, :darwin} -> :darwin
{:unix, :linux} -> :linux
{:unix, other} -> other
end
end

defp detect_cpu do
arch =
:erlang.system_info(:system_architecture)
|> to_string()
|> String.downcase()
|> String.split("-")
|> List.first()

case arch do
"x86_64" -> :x86_64
"aarch64" -> :aarch64
"arm64" -> :aarch64
other -> other
end
end
end
16 changes: 16 additions & 0 deletions test/mix/tasks/burrito.dinein_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
defmodule Mix.Tasks.Burrito.DineinTest do
use ExUnit.Case, async: true

alias Mix.Tasks.Burrito.Dinein

test "rejects extra positional arguments" do
assert_raise Mix.Error, "Usage: mix burrito.dinein [--target TARGET]", fn ->
Dinein.run(["extra"])
end
end

test "detect_target! returns a known supported target for the current host" do
target = Dinein.detect_target!()
assert target in ~w[macos_aarch64 linux_x86_64 windows_x86_64]
end
end