%%% @doc Meta-provider that dynamically compiles providers
|
|
%%% to run aliased commands.
|
|
%%%
|
|
%%% This is hackish and out-there, but this module has graduated
|
|
%%% from a plugin at https://github.com/tsloughter/rebar_alias after
|
|
%%% years of stability. Only some error checks were added
|
|
-module(rebar_prv_alias).
|
|
|
|
-export([init/1]).
|
|
-include("rebar.hrl").
|
|
|
|
%% ===================================================================
|
|
%% Public API
|
|
%% ===================================================================
|
|
-spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
|
|
init(State) ->
|
|
Aliases = rebar_state:get(State, alias, []),
|
|
lists:foldl(fun({Alias, Cmds}, {ok, StateAcc}) ->
|
|
case validate_provider(Alias, Cmds, State) of
|
|
true -> init_alias(Alias, Cmds, StateAcc);
|
|
false -> {ok, State}
|
|
end
|
|
end, {ok, State}, Aliases).
|
|
|
|
init_alias(Alias, Cmds, State) ->
|
|
Module = list_to_atom("rebar_prv_alias_" ++ atom_to_list(Alias)),
|
|
|
|
MF = module(Module),
|
|
EF = exports(),
|
|
FF = do_func(Cmds),
|
|
|
|
{ok, _, Bin} = compile:forms([MF, EF, FF]),
|
|
code:load_binary(Module, "none", Bin),
|
|
|
|
Provider = providers:create([
|
|
{name, Alias},
|
|
{module, Module},
|
|
{bare, true},
|
|
{deps, []},
|
|
{example, example(Alias)},
|
|
{opts, []},
|
|
{short_desc, desc(Cmds)},
|
|
{desc, desc(Cmds)}
|
|
]),
|
|
{ok, rebar_state:add_provider(State, Provider)}.
|
|
|
|
validate_provider(Alias, Cmds, State) ->
|
|
%% This would be caught and prevented anyway, but the warning
|
|
%% is friendlier
|
|
case providers:get_provider(Alias, rebar_state:providers(State)) of
|
|
not_found ->
|
|
%% check for circular deps in the alias.
|
|
case not proplists:is_defined(Alias, Cmds) of
|
|
true -> true;
|
|
false ->
|
|
?WARN("Alias ~p contains itself and would never "
|
|
"terminate. It will be ignored.",
|
|
[Alias]),
|
|
false
|
|
end;
|
|
_ ->
|
|
?WARN("Alias ~p is already the name of a command in "
|
|
"the default namespace and will be ignored.",
|
|
[Alias]),
|
|
false
|
|
end.
|
|
|
|
|
|
example(Alias) ->
|
|
"rebar3 " ++ atom_to_list(Alias).
|
|
|
|
desc(Cmds) ->
|
|
"Equivalent to running: rebar3 do "
|
|
++ rebar_string:join(lists:map(fun to_desc/1, Cmds), ",").
|
|
|
|
to_desc({Cmd, Args}) ->
|
|
atom_to_list(Cmd) ++ " " ++ Args;
|
|
to_desc(Cmd) ->
|
|
atom_to_list(Cmd).
|
|
|
|
module(Name) ->
|
|
{attribute, 1, module, Name}.
|
|
|
|
exports() ->
|
|
{attribute, 1, export, [{do, 1}]}.
|
|
|
|
do_func(Cmds) ->
|
|
{function, 1, do, 1,
|
|
[{clause, 1,
|
|
[{var, 1, 'State'}],
|
|
[],
|
|
[{call, 1,
|
|
{remote, 1, {atom, 1, rebar_prv_do}, {atom, 1, do_tasks}},
|
|
[make_args(Cmds), {var, 1, 'State'}]}]}]}.
|
|
|
|
make_args(Cmds) ->
|
|
make_list(
|
|
lists:map(fun make_tuple/1,
|
|
lists:map(fun make_arg/1, Cmds))).
|
|
|
|
make_arg({Cmd, Args}) ->
|
|
{make_string(Cmd), make_list([make_string(A) || A <- split_args(Args)])};
|
|
make_arg(Cmd) ->
|
|
{make_string(Cmd), make_list([])}.
|
|
|
|
make_tuple(Tuple) ->
|
|
{tuple, 1, tuple_to_list(Tuple)}.
|
|
|
|
make_list(List) ->
|
|
lists:foldr(
|
|
fun(Elem, Acc) -> {cons, 1, Elem, Acc} end,
|
|
{nil, 1},
|
|
List).
|
|
|
|
make_string(Atom) when is_atom(Atom) ->
|
|
make_string(atom_to_list(Atom));
|
|
make_string(String) when is_list(String) ->
|
|
{string, 1, String}.
|
|
|
|
%% In case someone used the long option format, the option needs to get
|
|
%% separated from its value.
|
|
split_args(Args) ->
|
|
rebar_string:lexemes(
|
|
lists:map(fun($=) -> 32; (C) -> C end, Args),
|
|
" ").
|