summaryrefslogtreecommitdiff
path: root/src/rebar_provider.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/rebar_provider.erl')
-rw-r--r--src/rebar_provider.erl139
1 files changed, 139 insertions, 0 deletions
diff --git a/src/rebar_provider.erl b/src/rebar_provider.erl
new file mode 100644
index 0000000..e8986f2
--- /dev/null
+++ b/src/rebar_provider.erl
@@ -0,0 +1,139 @@
+-module(rebar_provider).
+
+%% API
+-export([new/2,
+ do/2,
+ impl/1,
+ get_provider/2,
+ get_target_providers/2,
+ format/1]).
+
+-export_type([t/0]).
+
+-include("rebar.hrl").
+
+%%%===================================================================
+%%% Types
+%%%===================================================================
+
+-opaque t() :: record(provider).
+
+-type provider_name() :: atom().
+
+-ifdef(have_callback_support).
+
+-callback init(rebar_config:config()) -> {ok, rebar_config:config()} | relx:error().
+-callback do(rebar_config:config()) -> {ok, rebar_config:config()} | relx:error().
+
+-else.
+
+%% In the case where R14 or lower is being used to compile the system
+%% we need to export a behaviour info
+-export([behaviour_info/1]).
+-spec behaviour_info(atom()) -> [{atom(), arity()}] | undefined.
+behaviour_info(callbacks) ->
+ [{init, 1},
+ {do, 1}];
+behaviour_info(_) ->
+ undefined.
+
+-endif.
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+
+%% @doc create a new provider object from the specified module. The
+%% module should implement the provider behaviour.
+%%
+%% @param ModuleName The module name.
+%% @param State0 The current state of the system
+-spec new(module(), rebar_config:config()) ->
+ {ok, rebar_config:config()} | relx:error().
+new(ModuleName, State0) when is_atom(ModuleName) ->
+ case code:which(ModuleName) of
+ non_existing ->
+ ?ERROR("Module ~p does not exist.", [ModuleName]);
+ _ ->
+ ModuleName:init(State0)
+ end.
+
+%% @doc Manipulate the state of the system, that new state
+%%
+%% @param Provider the provider object
+%% @param State the current state of the system
+-spec do(Provider::t(), rebar_config:config()) ->
+ {ok, rebar_config:config()} | relx:error().
+do(Provider, State) ->
+ (Provider#provider.provider_impl):do(State).
+
+%%% @doc get the name of the module that implements the provider
+%%% @param Provider the provider object
+-spec impl(Provider::t()) -> module().
+impl(Provider) ->
+ Provider#provider.name.
+
+%% @doc print the provider module name
+%%
+%% @param T - The provider
+%% @return An iolist describing the provider
+-spec format(t()) -> iolist().
+format(#provider{provider_impl=Mod}) ->
+ erlang:atom_to_list(Mod).
+
+get_target_providers(Targets, State) ->
+ Providers = rebar_config:providers(State),
+ TargetProviders = lists:filter(fun(#provider{provides=T}) ->
+ lists:member(T, Targets)
+ end, Providers),
+ process_deps(TargetProviders, Providers).
+
+-spec get_provider(provider_name(), [t()]) -> t().
+get_provider(ProviderName, [Provider = #provider{name = ProviderName} | _]) ->
+ Provider;
+get_provider(ProviderName, [_ | Rest]) ->
+ get_provider(ProviderName, Rest);
+get_provider(_ProviderName, _) ->
+ [].
+
+process_deps([], _Providers) ->
+ [];
+process_deps(TargetProviders, Providers) ->
+ DepChain = lists:flatmap(fun(Provider) ->
+ {DC, _, _} = process_deps(Provider, Providers, []),
+ DC
+ end, TargetProviders),
+ ['NONE' | Rest] =
+ reorder_providers(lists:flatten([{'NONE', P#provider.name} || P <- TargetProviders] ++ DepChain)),
+ Rest.
+
+process_deps(Provider, Providers, Seen) ->
+ case lists:member(Provider, Seen) of
+ true ->
+ {[], Providers, Seen};
+ false ->
+ Deps = Provider#provider.deps,
+ DepList = lists:map(fun(Dep) ->
+ {Dep, Provider#provider.name}
+ end, Deps),
+ {NewDeps, _, NewSeen} =
+ lists:foldl(fun(Arg, Acc) ->
+ process_dep(Arg, Acc)
+ end,
+ {[], Providers, Seen}, Deps),
+ {[DepList | NewDeps], Providers, NewSeen}
+ end.
+
+process_dep(ProviderName, {Deps, Providers, Seen}) ->
+ Provider = get_provider(ProviderName, Providers),
+ {NewDeps, _, NewSeen} = process_deps(Provider, Providers, [ProviderName | Seen]),
+ {[Deps | NewDeps], Providers, NewSeen}.
+
+%% @doc Reorder the providers according to thier dependency set.
+reorder_providers(OProviderList) ->
+ case rebar_topo:sort(OProviderList) of
+ {ok, ProviderList} ->
+ ProviderList;
+ {cycle, _} ->
+ ?ERROR("There was a cycle in the provider list. Unable to complete build!", [])
+ end.