- %%% @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).
-
- -dialyzer([{no_opaque, init_alias/3}, {no_return, init_alias/3}]). % warnings relate to use of opaque structures in :forms
- 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.
-
- -dialyzer({no_unused, example/1}). % required since we suppress warnings for init_alias/3
- example(Alias) ->
- "rebar3 " ++ atom_to_list(Alias).
-
- -dialyzer({no_unused, desc/1}). % required since we suppress warnings for init_alias/3
- desc(Cmds) ->
- "Equivalent to running: rebar3 do "
- ++ rebar_string:join(lists:map(fun to_desc/1, Cmds), ",").
-
- to_desc({Cmd, Args}) when is_list(Args) ->
- atom_to_list(Cmd) ++ " " ++ Args;
- to_desc({Namespace, Cmd}) ->
- atom_to_list(Namespace) ++ " " ++ atom_to_list(Cmd);
- to_desc({Namespace, Cmd, Args}) ->
- atom_to_list(Namespace) ++ " " ++ 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({Namespace, Command, Args}) when is_atom(Namespace), is_atom(Command) ->
- {make_atom(Namespace),
- make_atom(Command),
- make_list([make_string(A) || A <- split_args(Args)])};
- make_arg({Namespace, Command}) when is_atom(Namespace), is_atom(Command) ->
- {make_atom(Namespace), make_atom(Command)};
- 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}.
-
- make_atom(Atom) when is_atom(Atom) ->
- {atom, 1, Atom}.
-
- %% 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),
- " ").
|