diff options
Diffstat (limited to 'src/rebar_provider.erl')
-rw-r--r-- | src/rebar_provider.erl | 139 |
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. |