% SPDX-FileCopyrightText: 2026 Dipl. Phys. Peer Stritzinger GmbH
%
% SPDX-License-Identifier: Apache-2.0
-module(rebar_calzone_prv).
-include_lib("rebar_calzone/src/rebar_calzone.hrl").
-export([init/1, do/1, format_error/1]).
%--- Includes ------------------------------------------------------------------
%--- Macros --------------------------------------------------------------------
%--- API -----------------------------------------------------------------------
-spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
init(State) ->
Provider = providers:create([
{name, calzone}, % The 'user friendly' name of the task
{module, ?MODULE}, % The module implementation of the task
{bare, true}, % The task can be run by the user, always true
{deps, [compile]}, % The list of dependencies
{example, "rebar3 calzone"}, % How to use the plugin
{opts, options()}, % list of options understood by the plugin
{short_desc, "Calzone"},
{desc, "Calzone"}
]),
{ok, rebar_state:add_provider(State, Provider)}.
-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
{Args, _ExtraArgs} = rebar_state:command_parsed_args(State),
TargetOS = proplists:get_value(os, Args),
TargetArch = proplists:get_value(arch, Args),
ConfigOptions = rebar_state:get(State, rebar_calzone),
Mode = get_option(mode, ConfigOptions, escript),
BaseDir = rebar_dir:base_dir(State),
CalzoneDir = filename:join(BaseDir, "calzone"),
[AppInfo] = rebar_state:project_apps(State),
Version = rebar_app_info:original_vsn(AppInfo),
Opts = #{
target_os => TargetOS,
target_arch => TargetArch,
mode => Mode,
base_dir => BaseDir,
calzone_dir => CalzoneDir,
version => Version
},
copy_otp_lib(CalzoneDir),
{Options2, State2} = release(Mode, Opts, State),
ok = rebar_calzone_record:record(Options2),
rebar_calzone_pack:bake(Options2),
{ok, State2}.
-spec format_error(any()) -> iolist().
format_error(Reason) ->
io_lib:format("~p", [Reason]).
%--- Private -------------------------------------------------------------------
options() ->
{Arch, OS} = system_architecture(),
[{os, undefined, "os", {atom, OS}, "Specify the target OS"},
{arch, undefined, "arch", {string, Arch},
"Specify the target Arch"}].
system_architecture() ->
[Arch, _, OS | _] = string:split(erlang:system_info(system_architecture), "-", all),
{arch_to_atom(Arch), os_to_atom(OS)}.
os_to_atom("linux") -> linux;
os_to_atom(["darwin", _]) -> macos.
arch_to_atom("aarch64") -> aarch64;
arch_to_atom("x86_64") -> x86_64.
copy_otp_lib(CalzoneDir) ->
RootDir = code:root_dir(),
ErtsVersion = erlang:system_info(version),
rebar_calzone_utils:copy_directory(RootDir, CalzoneDir, ["bin"]),
file:delete(filename:join([CalzoneDir, "bin", "empd"])),
BinEscript = filename:join([CalzoneDir, "bin", "escript"]),
% Making bin/escript executable
ok = file:change_mode(BinEscript, 8#755),
rebar_calzone_utils:copy_directory(RootDir, CalzoneDir, ["erts-" ++ ErtsVersion]),
rebar_calzone_utils:copy_directory(RootDir, CalzoneDir, ["lib"]).
release(escript, #{base_dir := BaseDir} = Opts, State) ->
#{calzone_dir := CalzoneDir} = Opts,
{ok, S} = rebar_prv_escriptize:do(State),
EscriptName = to_list(rebar_state:get(State, escript_name)),
BinDir = filename:join([BaseDir, "bin"]),
EscriptPath = filename:join([BinDir, EscriptName]),
EscriptDest = filename:join([CalzoneDir, "bin", EscriptName]),
{ok, _} = file:copy(EscriptPath, EscriptDest),
{Opts#{dir => BinDir,
name => EscriptName}, S};
release(release, #{base_dir := BaseDir} = Opts, State) ->
{ok, S} = rebar_prv_release:do(State),
Relx = rebar_state:get(S, relx, []),
{release, {RelName, _}, [_]} = lists:keyfind(release, 1, Relx),
{Opts#{dir => filename:join([BaseDir, "rel", RelName]),
name => atom_to_list(RelName)}, S}.
-spec get_option(atom(), proplists:proplist(), term()) -> term().
get_option(Option, Options, Default) ->
case lists:keyfind(Option, 1, Options) of
{Option, Value} -> Value;
false -> Default
end.
to_list(Name) when is_list(Name) -> Name;
to_list(Name) when is_atom(Name) -> atom_to_list(Name);
to_list(Name) when is_binary(Name) -> binary_to_list(Name).