defmodule Mix.Tasks.Marea.Build do
@moduledoc """
Builds the marea escript and wraps it as a self-contained executable.
This task must be run from the marea source project itself.
Dependent projects get `./marea` via `marea setup init-umbrella`, or
by symlinking the shipped `priv/marea` binary manually
(`ln -s /path/to/marea .`).
"""
@shortdoc "Build self-contained marea executable (run from marea source)"
use Mix.Task
@escript_name "marea"
@header ~S"""
#!/bin/sh
_TMP="${TMPDIR:-/tmp}/.marea_$$"
tail -n +10 "$0" > "$_TMP"
escript "$_TMP" "$@"
_rc=$?
rm -f "$_TMP"
[ $_rc -ne 0 ] && exit $_rc
[ -x .marea/next_cmd ] && exec .marea/next_cmd
exit 0
"""
@impl true
def run(args) do
name = project_escript_name()
# Remove existing symlink before building so escript.build writes a real file
if File.exists?(name), do: File.rm!(name)
Mix.Task.run("escript.build", args)
escript_data = File.read!(name)
File.write!(name, @header <> escript_data)
File.chmod!(name, 0o755)
# Copy to priv/marea for distribution
priv_path = Path.join("priv", name)
File.cp!(name, priv_path)
Mix.shell().info("Copied to #{priv_path}")
# Replace local binary with symlink to priv
File.rm!(name)
File.ln_s!(priv_path, name)
Mix.shell().info("Linked ./#{name} -> #{priv_path}")
end
defp project_escript_name do
config = Mix.Project.config()
case get_in(config, [:escript, :name]) do
nil -> config[:app] |> to_string()
name -> to_string(name)
end
end
@doc "The fixed escript name (`\"marea\"`)."
@spec escript_name() :: String.t()
def escript_name, do: @escript_name
@doc "The shell wrapper that prefixes the built escript bytes."
@spec header() :: String.t()
def header, do: @header
end