From 553a579b36fe0fb4a8bf464cd282d43c07d4e192 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Tue, 5 Dec 2017 07:47:07 -0500 Subject: Alias plugin promoted to built-in command - Uses the code at https://github.com/tsloughter/rebar_alias and brings it within rebar3 - adds safety checks to prevent redefining built-in commands or obvious circular dependencies between commands (indirect circular deps are still possible) - adds tests - adds a systest to ensure no clash with the existing plugin --- src/rebar_prv_alias.erl | 102 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 src/rebar_prv_alias.erl (limited to 'src/rebar_prv_alias.erl') diff --git a/src/rebar_prv_alias.erl b/src/rebar_prv_alias.erl new file mode 100644 index 0000000..2a03668 --- /dev/null +++ b/src/rebar_prv_alias.erl @@ -0,0 +1,102 @@ +%%% @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({Cmd, Args}) -> + atom_to_list(Cmd) ++ " " ++ Args; + (Cmd) -> + atom_to_list(Cmd) + end, Cmds), ","). + +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}}, + [to_args(Cmds),{var,1,'State'}]}]}]}. + + +to_args([]) -> + {nil,1}; +to_args([{Cmd, Args} | Rest]) -> + {cons,1,{tuple,1,[{string,1,atom_to_list(Cmd)},{string,1,Args}]}, to_args(Rest)}; +to_args([Cmd | Rest]) -> + {cons,1,{tuple,1,[{string,1,atom_to_list(Cmd)},{nil,1}]}, to_args(Rest)}. + -- cgit v1.1 From cc70e4fe2ff7b3e318d353972021e548fc513440 Mon Sep 17 00:00:00 2001 From: Tobias Schlager Date: Wed, 24 Jan 2018 12:12:40 +0100 Subject: Issue #1704: Fix alias provider argument passing The way arguments are passed by the alias provider is not compatible with all rebar providers/commands. Especially the release (relx) provider does not like getting its arguments as a plain string. It expects its arguments in a pre-parsed format as returned by getopt:parse/2. Other commands, e.g. eunit, seem to be fine with both ways of argument passing. Therefore, this fix changes the alias provider argument passing to the getopt format in general. --- src/rebar_prv_alias.erl | 69 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 23 deletions(-) (limited to 'src/rebar_prv_alias.erl') diff --git a/src/rebar_prv_alias.erl b/src/rebar_prv_alias.erl index 2a03668..736417b 100644 --- a/src/rebar_prv_alias.erl +++ b/src/rebar_prv_alias.erl @@ -70,33 +70,56 @@ example(Alias) -> "rebar3 " ++ atom_to_list(Alias). desc(Cmds) -> - "Equivalent to running: rebar3 do " ++ - rebar_string:join(lists:map(fun({Cmd, Args}) -> - atom_to_list(Cmd) ++ " " ++ Args; - (Cmd) -> - atom_to_list(Cmd) - end, 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}. + {attribute, 1, module, Name}. exports() -> - {attribute,1,export,[{do,1}]}. + {attribute, 1, export, [{do, 1}]}. do_func(Cmds) -> - {function,1,do,1, - [{clause,1, - [{var,1,'State'}], + {function, 1, do, 1, + [{clause, 1, + [{var, 1, 'State'}], [], - [{call,1, - {remote,1,{atom,1,rebar_prv_do},{atom,1,do_tasks}}, - [to_args(Cmds),{var,1,'State'}]}]}]}. - - -to_args([]) -> - {nil,1}; -to_args([{Cmd, Args} | Rest]) -> - {cons,1,{tuple,1,[{string,1,atom_to_list(Cmd)},{string,1,Args}]}, to_args(Rest)}; -to_args([Cmd | Rest]) -> - {cons,1,{tuple,1,[{string,1,atom_to_list(Cmd)},{nil,1}]}, to_args(Rest)}. - + [{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), + " "). -- cgit v1.1 From 5facb035fe67a0e2683c6460833ac1ec8baa8c82 Mon Sep 17 00:00:00 2001 From: tothlac Date: Sun, 11 Nov 2018 17:12:41 +0100 Subject: Support alias format {Namespace, Cmd} and {Namespace, Cmd, Args} (#1940) --- src/rebar_prv_alias.erl | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'src/rebar_prv_alias.erl') diff --git a/src/rebar_prv_alias.erl b/src/rebar_prv_alias.erl index 736417b..ce56f29 100644 --- a/src/rebar_prv_alias.erl +++ b/src/rebar_prv_alias.erl @@ -73,8 +73,12 @@ desc(Cmds) -> "Equivalent to running: rebar3 do " ++ rebar_string:join(lists:map(fun to_desc/1, Cmds), ","). -to_desc({Cmd, Args}) -> +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). @@ -98,6 +102,12 @@ make_args(Cmds) -> 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) -> @@ -117,6 +127,9 @@ make_string(Atom) when is_atom(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) -> -- cgit v1.1