You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

138 line
4.3 KiB

  1. %%% @doc Meta-provider that dynamically compiles providers
  2. %%% to run aliased commands.
  3. %%%
  4. %%% This is hackish and out-there, but this module has graduated
  5. %%% from a plugin at https://github.com/tsloughter/rebar_alias after
  6. %%% years of stability. Only some error checks were added
  7. -module(rebar_prv_alias).
  8. -export([init/1]).
  9. -include("rebar.hrl").
  10. %% ===================================================================
  11. %% Public API
  12. %% ===================================================================
  13. -spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
  14. init(State) ->
  15. Aliases = rebar_state:get(State, alias, []),
  16. lists:foldl(fun({Alias, Cmds}, {ok, StateAcc}) ->
  17. case validate_provider(Alias, Cmds, State) of
  18. true -> init_alias(Alias, Cmds, StateAcc);
  19. false -> {ok, State}
  20. end
  21. end, {ok, State}, Aliases).
  22. init_alias(Alias, Cmds, State) ->
  23. Module = list_to_atom("rebar_prv_alias_" ++ atom_to_list(Alias)),
  24. MF = module(Module),
  25. EF = exports(),
  26. FF = do_func(Cmds),
  27. {ok, _, Bin} = compile:forms([MF, EF, FF]),
  28. code:load_binary(Module, "none", Bin),
  29. Provider = providers:create([
  30. {name, Alias},
  31. {module, Module},
  32. {bare, true},
  33. {deps, []},
  34. {example, example(Alias)},
  35. {opts, []},
  36. {short_desc, desc(Cmds)},
  37. {desc, desc(Cmds)}
  38. ]),
  39. {ok, rebar_state:add_provider(State, Provider)}.
  40. validate_provider(Alias, Cmds, State) ->
  41. %% This would be caught and prevented anyway, but the warning
  42. %% is friendlier
  43. case providers:get_provider(Alias, rebar_state:providers(State)) of
  44. not_found ->
  45. %% check for circular deps in the alias.
  46. case not proplists:is_defined(Alias, Cmds) of
  47. true -> true;
  48. false ->
  49. ?WARN("Alias ~p contains itself and would never "
  50. "terminate. It will be ignored.",
  51. [Alias]),
  52. false
  53. end;
  54. _ ->
  55. ?WARN("Alias ~p is already the name of a command in "
  56. "the default namespace and will be ignored.",
  57. [Alias]),
  58. false
  59. end.
  60. example(Alias) ->
  61. "rebar3 " ++ atom_to_list(Alias).
  62. desc(Cmds) ->
  63. "Equivalent to running: rebar3 do "
  64. ++ rebar_string:join(lists:map(fun to_desc/1, Cmds), ",").
  65. to_desc({Cmd, Args}) when is_list(Args) ->
  66. atom_to_list(Cmd) ++ " " ++ Args;
  67. to_desc({Namespace, Cmd}) ->
  68. atom_to_list(Namespace) ++ " " ++ atom_to_list(Cmd);
  69. to_desc({Namespace, Cmd, Args}) ->
  70. atom_to_list(Namespace) ++ " " ++ atom_to_list(Cmd) ++ " " ++ Args;
  71. to_desc(Cmd) ->
  72. atom_to_list(Cmd).
  73. module(Name) ->
  74. {attribute, 1, module, Name}.
  75. exports() ->
  76. {attribute, 1, export, [{do, 1}]}.
  77. do_func(Cmds) ->
  78. {function, 1, do, 1,
  79. [{clause, 1,
  80. [{var, 1, 'State'}],
  81. [],
  82. [{call, 1,
  83. {remote, 1, {atom, 1, rebar_prv_do}, {atom, 1, do_tasks}},
  84. [make_args(Cmds), {var, 1, 'State'}]}]}]}.
  85. make_args(Cmds) ->
  86. make_list(
  87. lists:map(fun make_tuple/1,
  88. lists:map(fun make_arg/1, Cmds))).
  89. make_arg({Namespace, Command, Args}) when is_atom(Namespace), is_atom(Command) ->
  90. {make_atom(Namespace),
  91. make_atom(Command),
  92. make_list([make_string(A) || A <- split_args(Args)])};
  93. make_arg({Namespace, Command}) when is_atom(Namespace), is_atom(Command) ->
  94. {make_atom(Namespace), make_atom(Command)};
  95. make_arg({Cmd, Args}) ->
  96. {make_string(Cmd), make_list([make_string(A) || A <- split_args(Args)])};
  97. make_arg(Cmd) ->
  98. {make_string(Cmd), make_list([])}.
  99. make_tuple(Tuple) ->
  100. {tuple, 1, tuple_to_list(Tuple)}.
  101. make_list(List) ->
  102. lists:foldr(
  103. fun(Elem, Acc) -> {cons, 1, Elem, Acc} end,
  104. {nil, 1},
  105. List).
  106. make_string(Atom) when is_atom(Atom) ->
  107. make_string(atom_to_list(Atom));
  108. make_string(String) when is_list(String) ->
  109. {string, 1, String}.
  110. make_atom(Atom) when is_atom(Atom) ->
  111. {atom, 1, Atom}.
  112. %% In case someone used the long option format, the option needs to get
  113. %% separated from its value.
  114. split_args(Args) ->
  115. rebar_string:lexemes(
  116. lists:map(fun($=) -> 32; (C) -> C end, Args),
  117. " ").