Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

162 lignes
5.8 KiB

  1. -module(rebar_provider).
  2. %% API
  3. -export([create/1,
  4. new/2,
  5. do/2,
  6. impl/1,
  7. get_provider/2,
  8. get_target_providers/2,
  9. help/1,
  10. format/1]).
  11. -export_type([t/0]).
  12. -include("rebar.hrl").
  13. %%%===================================================================
  14. %%% Types
  15. %%%===================================================================
  16. -type t() :: record(provider).
  17. -type provider_name() :: atom().
  18. -callback init(rebar_state:t()) -> {ok, rebar_state:t()}.
  19. -callback do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
  20. %%%===================================================================
  21. %%% API
  22. %%%===================================================================
  23. %% @doc create a new provider object from the specified module. The
  24. %% module should implement the provider behaviour.
  25. %%
  26. %% @param ModuleName The module name.
  27. %% @param State0 The current state of the system
  28. -spec new(module(), rebar_state:t()) -> {ok, rebar_state:t()}.
  29. new(ModuleName, State) when is_atom(ModuleName) ->
  30. case code:which(ModuleName) of
  31. non_existing ->
  32. ?ERROR("Module ~p does not exist.", [ModuleName]),
  33. {ok, State};
  34. _ ->
  35. ModuleName:init(State)
  36. end.
  37. -spec create(list()) -> t().
  38. create(Attrs) ->
  39. #provider{name=proplists:get_value(name, Attrs, undefined)
  40. ,provider_impl=proplists:get_value(provider_impl, Attrs, undefined)
  41. ,bare=proplists:get_value(bare, Attrs, false)
  42. ,deps=proplists:get_value(deps, Attrs, [])
  43. ,desc=proplists:get_value(desc, Attrs, "")
  44. ,short_desc=proplists:get_value(short_desc, Attrs, "")
  45. ,example=proplists:get_value(example, Attrs, "")
  46. ,opts=proplists:get_value(opts, Attrs, [])}.
  47. %% @doc Manipulate the state of the system, that new state
  48. %%
  49. %% @param Provider the provider object
  50. %% @param State the current state of the system
  51. -spec do(Provider::t(), rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
  52. do(Provider, State) ->
  53. {PreHooks, PostHooks} = rebar_state:hooks(State, Provider#provider.name),
  54. run_all([PreHooks++Provider | PostHooks], State).
  55. -spec run_all([t()], rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
  56. run_all([], State) ->
  57. {ok, State};
  58. run_all([Provider | Rest], State) ->
  59. case (Provider#provider.provider_impl):do(State) of
  60. {ok, State1} ->
  61. run_all(Rest, State1);
  62. {error, Error} ->
  63. {error, Error}
  64. end.
  65. %%% @doc get the name of the module that implements the provider
  66. %%% @param Provider the provider object
  67. -spec impl(Provider::t()) -> module().
  68. impl(Provider) ->
  69. Provider#provider.name.
  70. help(State) ->
  71. Providers = rebar_state:providers(State),
  72. Help = lists:sort([{ec_cnv:to_list(P#provider.name), P#provider.short_desc} || P <- Providers,
  73. P#provider.bare =/= true]),
  74. Longest = lists:max([length(X) || {X, _} <- Help]),
  75. lists:foreach(fun({Name, ShortDesc}) ->
  76. Length = length(Name),
  77. Spacing = lists:duplicate(Longest - Length + 8, " "),
  78. io:format("~s~s~s~n", [Name, Spacing, ShortDesc])
  79. end, Help).
  80. %% @doc print the provider module name
  81. %%
  82. %% @param T - The provider
  83. %% @return An iolist describing the provider
  84. -spec format(t()) -> iolist().
  85. format(#provider{name=Name}) ->
  86. atom_to_list(Name).
  87. get_target_providers(Target, State) ->
  88. Providers = rebar_state:providers(State),
  89. TargetProviders = lists:filter(fun(#provider{name=T}) when T =:= Target->
  90. true;
  91. (_) ->
  92. false
  93. end, Providers),
  94. process_deps(TargetProviders, Providers).
  95. -spec get_provider(provider_name(), [t()]) -> t().
  96. get_provider(ProviderName, [Provider = #provider{name = ProviderName} | _]) ->
  97. Provider;
  98. get_provider(ProviderName, [_ | Rest]) ->
  99. get_provider(ProviderName, Rest);
  100. get_provider(_ProviderName, _) ->
  101. [].
  102. process_deps([], _Providers) ->
  103. [];
  104. process_deps(TargetProviders, Providers) ->
  105. DepChain = lists:flatmap(fun(Provider) ->
  106. {DC, _, _} = process_deps(Provider, Providers, []),
  107. DC
  108. end, TargetProviders),
  109. ['NONE' | Rest] =
  110. reorder_providers(lists:flatten([{'NONE', P#provider.name} || P <- TargetProviders] ++ DepChain)),
  111. Rest.
  112. process_deps(Provider, Providers, Seen) ->
  113. case lists:member(Provider, Seen) of
  114. true ->
  115. {[], Providers, Seen};
  116. false ->
  117. Deps = Provider#provider.deps,
  118. DepList = lists:map(fun(Dep) ->
  119. {Dep, Provider#provider.name}
  120. end, Deps),
  121. {NewDeps, _, NewSeen} =
  122. lists:foldl(fun(Arg, Acc) ->
  123. process_dep(Arg, Acc)
  124. end,
  125. {[], Providers, Seen}, Deps),
  126. {[DepList | NewDeps], Providers, NewSeen}
  127. end.
  128. process_dep(ProviderName, {Deps, Providers, Seen}) ->
  129. Provider = get_provider(ProviderName, Providers),
  130. {NewDeps, _, NewSeen} = process_deps(Provider, Providers, [ProviderName | Seen]),
  131. {[Deps | NewDeps], Providers, NewSeen}.
  132. %% @doc Reorder the providers according to thier dependency set.
  133. reorder_providers(OProviderList) ->
  134. case rebar_topo:sort(OProviderList) of
  135. {ok, ProviderList} ->
  136. ProviderList;
  137. {error, {cycle, _}} ->
  138. ?ERROR("There was a cycle in the provider list. Unable to complete build!", [])
  139. end.