Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

140 Zeilen
4.5 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. -dialyzer([{no_opaque, init_alias/3}, {no_return, init_alias/3}]). % warnings relate to use of opaque structures in :forms
  23. init_alias(Alias, Cmds, State) ->
  24. Module = list_to_atom("rebar_prv_alias_" ++ atom_to_list(Alias)),
  25. MF = module(Module),
  26. EF = exports(),
  27. FF = do_func(Cmds),
  28. {ok, _, Bin} = compile:forms([MF, EF, FF]),
  29. code:load_binary(Module, "none", Bin),
  30. Provider = providers:create([
  31. {name, Alias},
  32. {module, Module},
  33. {bare, true},
  34. {deps, []},
  35. {example, example(Alias)},
  36. {opts, []},
  37. {short_desc, desc(Cmds)},
  38. {desc, desc(Cmds)}
  39. ]),
  40. {ok, rebar_state:add_provider(State, Provider)}.
  41. validate_provider(Alias, Cmds, State) ->
  42. %% This would be caught and prevented anyway, but the warning
  43. %% is friendlier
  44. case providers:get_provider(Alias, rebar_state:providers(State)) of
  45. not_found ->
  46. %% check for circular deps in the alias.
  47. case not proplists:is_defined(Alias, Cmds) of
  48. true -> true;
  49. false ->
  50. ?WARN("Alias ~p contains itself and would never "
  51. "terminate. It will be ignored.",
  52. [Alias]),
  53. false
  54. end;
  55. _ ->
  56. ?WARN("Alias ~p is already the name of a command in "
  57. "the default namespace and will be ignored.",
  58. [Alias]),
  59. false
  60. end.
  61. -dialyzer({no_unused, example/1}). % required since we suppress warnings for init_alias/3
  62. example(Alias) ->
  63. "rebar3 " ++ atom_to_list(Alias).
  64. -dialyzer({no_unused, desc/1}). % required since we suppress warnings for init_alias/3
  65. desc(Cmds) ->
  66. "Equivalent to running: rebar3 do "
  67. ++ rebar_string:join(lists:map(fun to_desc/1, Cmds), ",").
  68. to_desc({Cmd, Args}) when is_list(Args) ->
  69. atom_to_list(Cmd) ++ " " ++ Args;
  70. to_desc({Namespace, Cmd}) ->
  71. atom_to_list(Namespace) ++ " " ++ atom_to_list(Cmd);
  72. to_desc({Namespace, Cmd, Args}) ->
  73. atom_to_list(Namespace) ++ " " ++ atom_to_list(Cmd) ++ " " ++ Args;
  74. to_desc(Cmd) ->
  75. atom_to_list(Cmd).
  76. module(Name) ->
  77. {attribute, 1, module, Name}.
  78. exports() ->
  79. {attribute, 1, export, [{do, 1}]}.
  80. do_func(Cmds) ->
  81. {function, 1, do, 1,
  82. [{clause, 1,
  83. [{var, 1, 'State'}],
  84. [],
  85. [{call, 1,
  86. {remote, 1, {atom, 1, rebar_prv_do}, {atom, 1, do_tasks}},
  87. [make_args(Cmds), {var, 1, 'State'}]}]}]}.
  88. make_args(Cmds) ->
  89. make_list(
  90. lists:map(fun make_tuple/1,
  91. lists:map(fun make_arg/1, Cmds))).
  92. make_arg({Namespace, Command, Args}) when is_atom(Namespace), is_atom(Command) ->
  93. {make_atom(Namespace),
  94. make_atom(Command),
  95. make_list([make_string(A) || A <- split_args(Args)])};
  96. make_arg({Namespace, Command}) when is_atom(Namespace), is_atom(Command) ->
  97. {make_atom(Namespace), make_atom(Command)};
  98. make_arg({Cmd, Args}) ->
  99. {make_string(Cmd), make_list([make_string(A) || A <- split_args(Args)])};
  100. make_arg(Cmd) ->
  101. {make_string(Cmd), make_list([])}.
  102. make_tuple(Tuple) ->
  103. {tuple, 1, tuple_to_list(Tuple)}.
  104. make_list(List) ->
  105. lists:foldr(
  106. fun(Elem, Acc) -> {cons, 1, Elem, Acc} end,
  107. {nil, 1},
  108. List).
  109. make_string(Atom) when is_atom(Atom) ->
  110. make_string(atom_to_list(Atom));
  111. make_string(String) when is_list(String) ->
  112. {string, 1, String}.
  113. make_atom(Atom) when is_atom(Atom) ->
  114. {atom, 1, Atom}.
  115. %% In case someone used the long option format, the option needs to get
  116. %% separated from its value.
  117. split_args(Args) ->
  118. rebar_string:lexemes(
  119. lists:map(fun($=) -> 32; (C) -> C end, Args),
  120. " ").