From 0516b405ccb98febcf94a8e94000f4a633569f59 Mon Sep 17 00:00:00 2001 From: Tristan Sloughter Date: Sun, 15 Jun 2014 19:18:14 -0500 Subject: provider task dependencies --- include/relx.hrl | 1 - rebar.config | 8 +- src/relx.app.src | 2 +- src/relx.erl | 57 ++++----- src/rlx_app_discovery.erl | 2 +- src/rlx_cmd_args.erl | 20 ++-- src/rlx_config.erl | 279 ++++++++++++++++++++++++++++++++++++++++++++ src/rlx_dscv_util.erl | 6 +- src/rlx_provider.erl | 117 ------------------- src/rlx_prv_archive.erl | 17 ++- src/rlx_prv_assembler.erl | 23 +++- src/rlx_prv_config.erl | 278 ------------------------------------------- src/rlx_prv_discover.erl | 18 ++- src/rlx_prv_overlay.erl | 16 ++- src/rlx_prv_release.erl | 18 ++- src/rlx_prv_relup.erl | 19 ++- src/rlx_rel_discovery.erl | 2 +- src/rlx_state.erl | 113 ++++++++++-------- src/rlx_topo.erl | 14 ++- test/rlx_command_SUITE.erl | 16 ++- test/rlx_discover_SUITE.erl | 17 +-- test/rlx_release_SUITE.erl | 21 ++-- 22 files changed, 521 insertions(+), 543 deletions(-) create mode 100644 src/rlx_config.erl delete mode 100644 src/rlx_provider.erl delete mode 100644 src/rlx_prv_config.erl diff --git a/include/relx.hrl b/include/relx.hrl index 43aec62..3194873 100644 --- a/include/relx.hrl +++ b/include/relx.hrl @@ -1,4 +1,3 @@ - %% Copyright 2012 Erlware, LLC. All Rights Reserved. %% %% This file is provided to you under the Apache License, diff --git a/rebar.config b/rebar.config index a250ce9..396c546 100644 --- a/rebar.config +++ b/rebar.config @@ -8,6 +8,9 @@ {erlware_commons, ".*", {git, "https://github.com/erlware/erlware_commons.git", {branch, "master"}}}, + {providers, ".*", + {git, "https://github.com/tsloughter/providers.git", + {branch, "master"}}}, {erlydtl, ".*", {git, "https://github.com/erlydtl/erlydtl.git", {tag, "0.9.0"}}}, @@ -22,6 +25,7 @@ %% Compiler Options ============================================================ {erl_opts, [{platform_define, "^[0-9]+", namespaced_types}, + {platform_define, "R14", no_callback_support}, debug_info, warnings_as_errors, inline]}. @@ -36,6 +40,6 @@ {erlydtl_opts, [{doc_root, "priv/templates"}, {compiler_options, [report, return, debug_info]}]}. {escript_incl_apps, - [getopt, erlware_commons, erlydtl]}. + [getopt, erlware_commons, erlydtl, providers]}. -{first_files, [rcl_provider]}. +{first_files, [rlx_provider]}. diff --git a/src/relx.app.src b/src/relx.app.src index c52958b..3e2b493 100644 --- a/src/relx.app.src +++ b/src/relx.app.src @@ -23,4 +23,4 @@ {vsn, "semver"}, {modules, []}, {registered, []}, - {applications, [kernel, stdlib, getopt, erlware_commons]}]}. + {applications, [kernel, stdlib, getopt, erlware_commons, providers]}]}. diff --git a/src/relx.erl b/src/relx.erl index 1364538..e9867a9 100644 --- a/src/relx.erl +++ b/src/relx.erl @@ -57,6 +57,7 @@ main(Args) -> true -> usage(); false -> + application:start(relx), do([{caller, command_line} | Options], NonOptions) end end; @@ -212,7 +213,7 @@ opt_spec_list() -> -spec format_error(Reason::term()) -> string(). format_error({invalid_return_value, Provider, Value}) -> - io_lib:format(lists:flatten([rlx_provider:format(Provider), " returned an invalid value ", + io_lib:format(lists:flatten([providers:format(Provider), " returned an invalid value ", io_lib:format("~p", [Value])]), []); format_error({opt_parse, {invalid_option, Opt}}) -> io_lib:format("invalid option ~s~n", [Opt]); @@ -240,16 +241,27 @@ run_relx_process(State) -> %% providers again and run the rest of them (because they could have been %% updated by the config process). run_providers(State0) -> - [ConfigProvider | _] = rlx_state:providers(State0), - case run_provider(ConfigProvider, {ok, State0}) of - Err = {error, _} -> - Err; + case rlx_config:do(State0) of {ok, State1} -> + Actions = rlx_state:actions(State0), + + AllProviders = rlx_state:providers(State1), + TargetProviders = lists:flatmap(fun(Target) -> + providers:get_target_providers(Target, AllProviders) + end, Actions), + Providers1 = lists:map(fun(P) -> + providers:get_provider(P, AllProviders) + end, TargetProviders), + + %% Unique Sort Providers + Providers2 = providers:process_deps(Providers1, AllProviders), + RootDir = rlx_state:root_dir(State1), ok = file:set_cwd(RootDir), - Providers = rlx_state:providers(State1), - Result = run_providers(ConfigProvider, Providers, State1), - handle_output(State1, rlx_state:caller(State1), Result) + Result = lists:foldl(fun run_provider/2, {ok, State1}, Providers2), + handle_output(State1, rlx_state:caller(State1), Result); + Err -> + Err end. handle_output(State, command_line, E={error, _}) -> @@ -260,33 +272,24 @@ handle_output(_State, command_line, _) -> handle_output(_State, api, Result) -> Result. -run_providers(ConfigProvider, Providers, State0) -> - case Providers of - [ConfigProvider | Rest] -> - %% IF the config provider is still the first provider do not run it - %% again just run the rest. - lists:foldl(fun run_provider/2, {ok, State0}, Rest); - _ -> - lists:foldl(fun run_provider/2, {ok, State0}, Providers) - end. - --spec run_provider(rlx_provider:t(), {ok, rlx_state:t()} | error()) -> +-spec run_provider(atom(), {ok, rlx_state:t()} | error()) -> {ok, rlx_state:t()} | error(). -run_provider(_Provider, Error = {error, _}) -> - Error; -run_provider(Provider, {ok, State0}) -> +run_provider(ProviderName, {ok, State0}) -> + Provider = providers:get_provider(ProviderName, rlx_state:providers(State0)), ec_cmd_log:debug(rlx_state:log(State0), "Running provider ~p~n", - [rlx_provider:impl(Provider)]), - case rlx_provider:do(Provider, State0) of + [providers:impl(Provider)]), + case providers:do(Provider, State0) of {ok, State1} -> ec_cmd_log:debug(rlx_state:log(State0), "Provider successfully run: ~p~n", - [rlx_provider:impl(Provider)]), + [providers:impl(Provider)]), {ok, State1}; E={error, _} -> ec_cmd_log:debug(rlx_state:log(State0), "Provider (~p) failed with: ~p~n", - [rlx_provider:impl(Provider), E]), + [providers:impl(Provider), E]), E - end. + end; +run_provider(_ProviderName, Error) -> + Error. -spec usage() -> ok. usage() -> diff --git a/src/rlx_app_discovery.erl b/src/rlx_app_discovery.erl index 4a5620b..6ac8d11 100644 --- a/src/rlx_app_discovery.erl +++ b/src/rlx_app_discovery.erl @@ -21,7 +21,7 @@ %%% @doc This provider uses the lib_dir setting of the state. It searches the %%% Lib Dirs looking for all OTP Applications that are available. When it finds %%% those OTP Applications it loads the information about them and adds them to -%%% the state of available apps. This implements the rlx_provider behaviour. +%%% the state of available apps. This implements the provider behaviour. -module(rlx_app_discovery). -export([do/2, diff --git a/src/rlx_cmd_args.erl b/src/rlx_cmd_args.erl index 5df9392..e117d8e 100644 --- a/src/rlx_cmd_args.erl +++ b/src/rlx_cmd_args.erl @@ -87,15 +87,15 @@ format_error({invalid_target, Target}) -> %%%=================================================================== %%% Internal Functions %%%=================================================================== --spec handle_config([getopt:option()], [atom()], proplists:proplist()) -> - {ok, {rlx_state:t(), [string()]}} | - relx:error(). +-spec handle_config(any(), [atom()], proplists:proplist()) -> + {ok, {rlx_state:t(), [string()]}} | relx:error(). handle_config(Opts, Targets, CommandLineConfig) -> - case validate_config(proplists:get_value(config, Opts, [])) of - Error = {error, _} -> - Error; - {ok, Config} -> - {ok, rlx_state:new(Config, CommandLineConfig, Targets)} + {ok, Config} = validate_config(proplists:get_value(config, Opts, [])), + case rlx_state:new(Config, CommandLineConfig, Targets) of + {error, Error} -> + {error, Error}; + State -> + {ok, State} end. -spec convert_targets([string()]) -> {ok, release | relup} | relx:error(). @@ -117,8 +117,8 @@ convert_targets(["tar" | T], Acc) -> convert_targets([Target | _T], _Acc) -> ?RLX_ERROR({invalid_target, Target}). --spec validate_config(file:filename() | undefined) -> - {ok, file:filename() | undefined} | relx:error(). +-spec validate_config(file:filename() | list() | undefined) -> + {ok, file:filename() | list() | undefined}. validate_config(undefined) -> {ok, undefined}; validate_config("") -> diff --git a/src/rlx_config.erl b/src/rlx_config.erl new file mode 100644 index 0000000..d4acb3c --- /dev/null +++ b/src/rlx_config.erl @@ -0,0 +1,279 @@ +%% -*- erlang-indent-level: 4; indent-tabs-mode: nil; fill-column: 80 -*- +%%% Copyright 2012 Erlware, LLC. All Rights Reserved. +%%% +%%% This file is provided to you under the Apache License, +%%% Version 2.0 (the "License"); you may not use this file +%%% except in compliance with the License. You may obtain +%%% a copy of the License at +%%% +%%% http://www.apache.org/licenses/LICENSE-2.0 +%%% +%%% Unless required by applicable law or agreed to in writing, +%%% software distributed under the License is distributed on an +%%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%%% KIND, either express or implied. See the License for the +%%% specific language governing permissions and limitations +%%% under the License. +%%%------------------------------------------------------------------- +%%% @author Eric Merritt +%%% @copyright 2011 Erlware, LLC. +%%% @doc +%%% A module that provides config parsing and support to the system +%%% @end +%%%------------------------------------------------------------------- +-module(rlx_config). + +%% API +-export([do/1, + format_error/1]). + +-include("relx.hrl"). + +%%%=================================================================== +%%% API +%%%=================================================================== + +%% @doc parse all the configs currently specified in the state, +%% populating the state as a result. +-spec do(rlx_state:t()) ->{ok, rlx_state:t()} | relx:error(). +do(State) -> + case rlx_state:config_file(State) of + [] -> + search_for_dominating_config(State); + undefined -> + search_for_dominating_config(State); + ConfigFile when erlang:is_list(ConfigFile) -> + load_config(ConfigFile, State) + end. + +-spec format_error(Reason::term()) -> iolist(). +format_error({consult, ConfigFile, Reason}) -> + io_lib:format("Unable to read file ~s: ~s", [ConfigFile, + file:format_error(Reason)]); +format_error({invalid_term, Term}) -> + io_lib:format("Invalid term in config file: ~p", [Term]). + +%%%=================================================================== +%%% Internal Functions +%%%=================================================================== +search_for_dominating_config({ok, Cwd}) -> + ConfigFile = filename:join(Cwd, "relx.config"), + case ec_file:exists(ConfigFile) of + true -> + {ok, ConfigFile}; + false -> + search_for_dominating_config(parent_dir(Cwd)) + end; +search_for_dominating_config({error, _}) -> + no_config; +search_for_dominating_config(State0) -> + {ok, Cwd} = file:get_cwd(), + case search_for_dominating_config({ok, Cwd}) of + {ok, Config} -> + %% we need to set the root dir on state as well + {ok, RootDir} = parent_dir(Config), + State1 = rlx_state:root_dir(State0, RootDir), + load_config(Config, rlx_state:config_file(State1, Config)); + no_config -> + {ok, State0} + end. + +%% @doc Given a directory returns the name of the parent directory. +-spec parent_dir(Filename::string()) -> + {ok, DirName::string()} | {error, no_parent_dir}. +parent_dir(Filename) -> + parent_dir(filename:split(Filename), []). + +%% @doc Given list of directories, splits the list and returns all dirs but the +%% last as a path. +-spec parent_dir([string()], [string()]) -> + {ok, DirName::string()} | {error, no_parent_dir}. +parent_dir([_H], []) -> + {error, no_parent_dir}; +parent_dir([], []) -> + {error, no_parent_dir}; +parent_dir([_H], Acc) -> + {ok, filename:join(lists:reverse(Acc))}; +parent_dir([H | T], Acc) -> + parent_dir(T, [H | Acc]). + +-spec load_config(file:filename(), rlx_state:t()) -> + {ok, rlx_state:t()} | relx:error(). +load_config(ConfigFile, State) -> + {ok, CurrentCwd} = file:get_cwd(), + ok = file:set_cwd(filename:dirname(ConfigFile)), + Result = case file:consult(ConfigFile) of + {error, Reason} -> + ?RLX_ERROR({consult, ConfigFile, Reason}); + {ok, Terms} -> + CliTerms = rlx_state:cli_args(State), + lists:foldl(fun load_terms/2, {ok, State}, merge_configs(CliTerms, Terms)) + end, + ok = file:set_cwd(CurrentCwd), + Result. + +-spec load_terms(term(), {ok, rlx_state:t()} | relx:error()) -> + {ok, rlx_state:t()} | relx:error(). +load_terms({default_release, {RelName, RelVsn}}, {ok, State}) -> + NewVsn = parse_vsn(RelVsn), + {ok, rlx_state:default_configured_release(State, RelName, NewVsn)}; +load_terms({paths, Paths}, {ok, State}) -> + code:add_pathsa([filename:absname(Path) || Path <- Paths]), + {ok, State}; +load_terms({default_libs, DefaultLibs}, {ok, State}) -> + State2 = rlx_state:put(State, + default_libs, + DefaultLibs), + {ok, State2}; +load_terms({system_libs, SystemLibs}, {ok, State}) -> + State2 = rlx_state:put(State, + system_libs, + SystemLibs), + {ok, State2}; +load_terms({lib_dirs, Dirs}, {ok, State}) -> + State2 = + rlx_state:add_lib_dirs(State, + [list_to_binary(Dir) || Dir <- rlx_util:wildcard_paths(Dirs)]), + {ok, State2}; +load_terms({hooks, Hooks}, {ok, State0}) -> + add_hooks(Hooks, State0); +load_terms({providers, Providers0}, {ok, State0}) -> + Providers1 = gen_providers(Providers0, State0), + case Providers1 of + {_, E={error, _}} -> + E; + {Providers3, {ok, State3}} -> + {ok, rlx_state:providers(State3, Providers3)} + end; +load_terms({add_providers, Providers0}, {ok, State0}) -> + Providers1 = gen_providers(Providers0, State0), + case Providers1 of + {_, E={error, _}} -> + E; + {Providers3, {ok, State1}} -> + ExistingProviders = rlx_state:providers(State1), + {ok, rlx_state:providers(State1, ExistingProviders ++ Providers3)} + end; +load_terms({skip_apps, SkipApps0}, {ok, State0}) -> + {ok, rlx_state:skip_apps(State0, SkipApps0)}; +load_terms({overrides, Overrides0}, {ok, State0}) -> + {ok, rlx_state:overrides(State0, Overrides0)}; +load_terms({dev_mode, DevMode}, {ok, State0}) -> + {ok, rlx_state:dev_mode(State0, DevMode)}; +load_terms({goals, Goals}, {ok, State0}) -> + {ok, rlx_state:goals(State0, Goals)}; +load_terms({upfrom, UpFrom}, {ok, State0}) -> + {ok, rlx_state:upfrom(State0, UpFrom)}; +load_terms({include_src, IncludeSrc}, {ok, State0}) -> + {ok, rlx_state:include_src(State0, IncludeSrc)}; +load_terms({release, {RelName, Vsn, {extend, RelName2}}, Applications}, {ok, State0}) -> + NewVsn = parse_vsn(Vsn), + Release0 = rlx_release:new(RelName, NewVsn), + ExtendRelease = rlx_state:get_configured_release(State0, RelName2, NewVsn), + Applications1 = rlx_release:goals(ExtendRelease), + case rlx_release:goals(Release0, + lists:umerge(lists:usort(Applications), + lists:usort(Applications1))) of + E={error, _} -> + E; + {ok, Release1} -> + {ok, rlx_state:add_configured_release(State0, Release1)} + end; +load_terms({release, {RelName, Vsn}, Applications}, {ok, State0}) -> + NewVsn = parse_vsn(Vsn), + Release0 = rlx_release:new(RelName, NewVsn), + case rlx_release:goals(Release0, Applications) of + E={error, _} -> + E; + {ok, Release1} -> + {ok, rlx_state:add_configured_release(State0, Release1)} + end; +load_terms({release, {RelName, Vsn}, {erts, ErtsVsn}, + Applications}, {ok, State}) -> + NewVsn = parse_vsn(Vsn), + Release0 = rlx_release:erts(rlx_release:new(RelName, NewVsn), ErtsVsn), + case rlx_release:goals(Release0, Applications) of + E={error, _} -> + E; + {ok, Release1} -> + {ok, rlx_state:add_configured_release(State, Release1)} + end; +load_terms({vm_args, VmArgs}, {ok, State}) -> + {ok, rlx_state:vm_args(State, filename:absname(VmArgs))}; +load_terms({sys_config, SysConfig}, {ok, State}) -> + case rlx_state:sys_config(State) of + undefined -> + {ok, rlx_state:sys_config(State, filename:absname(SysConfig))}; + _ -> + {ok, State} + end; +load_terms({root_dir, Root}, {ok, State}) -> + {ok, rlx_state:root_dir(State, filename:absname(Root))}; +load_terms({output_dir, OutputDir}, {ok, State}) -> + {ok, rlx_state:base_output_dir(State, filename:absname(OutputDir))}; +load_terms({overlay_vars, OverlayVars}, {ok, State}) -> + CurrentOverlayVars = rlx_state:get(State, overlay_vars), + NewOverlayVars0 = list_of_overlay_vars_files(OverlayVars), + NewOverlayVars1 = lists:umerge(lists:usort(NewOverlayVars0), lists:usort(CurrentOverlayVars)), + {ok, rlx_state:put(State, overlay_vars, NewOverlayVars1)}; +load_terms({Name, Value}, {ok, State}) + when erlang:is_atom(Name) -> + {ok, rlx_state:put(State, Name, Value)}; +load_terms(_, Error={error, _}) -> + Error; +load_terms(InvalidTerm, _) -> + ?RLX_ERROR({invalid_term, InvalidTerm}). + +-spec gen_providers([module()], rlx_state:t()) -> + {[providers:t()], {ok, rlx_state:t()} | relx:error()}. +gen_providers(Providers, State) -> + lists:foldl(fun(ProviderName, {Providers1, {ok, State1}}) -> + {Provider, State2} = providers:new(ProviderName, State1), + {[Provider | Providers1], State2}; + (_, E={_, {error, _}}) -> + E + end, {[], {ok, State}}, Providers). + +add_hooks(Hooks, State) -> + {ok, lists:foldl(fun({pre, Target, Hook}, StateAcc) -> + rlx_state:prepend_hook(StateAcc, Target, Hook); + ({post, Target, Hook}, StateAcc) -> + rlx_state:append_hook(StateAcc, Target, Hook) + end, State, Hooks)}. + +list_of_overlay_vars_files(undefined) -> + []; +list_of_overlay_vars_files([]) -> + []; +list_of_overlay_vars_files([H | _]=FileNames) when erlang:is_list(H) -> + FileNames; +list_of_overlay_vars_files(FileName) when is_list(FileName) -> + [FileName]. + +merge_configs([], ConfigTerms) -> + ConfigTerms; +merge_configs([{_Key, undefined} | CliTerms], ConfigTerms) -> + merge_configs(CliTerms, ConfigTerms); +merge_configs([{_Key, []} | CliTerms], ConfigTerms) -> + merge_configs(CliTerms, ConfigTerms); +merge_configs([{Key, Value} | CliTerms], ConfigTerms) -> + case Key of + X when X =:= lib_dirs + ; X =:= goals + ; X =:= overlay_vars + ; X =:= overrides -> + case lists:keyfind(Key, 1, ConfigTerms) of + {Key, Value2} -> + MergedValue = lists:umerge([Value, Value2]), + merge_configs(CliTerms, lists:keyreplace(Key, 1, ConfigTerms, {Key, MergedValue})); + false -> + merge_configs(CliTerms, [{Key, Value} | ConfigTerms]) + end; + _ -> + merge_configs(CliTerms, lists:keystore(Key, 1, ConfigTerms, {Key, Value})) + end. + +parse_vsn(Vsn) when Vsn =:= semver ; Vsn =:= "semver" -> + binary_to_list(ec_git_vsn:vsn([])); +parse_vsn(Vsn) -> + Vsn. diff --git a/src/rlx_dscv_util.erl b/src/rlx_dscv_util.erl index aa642b8..fefdbce 100644 --- a/src/rlx_dscv_util.erl +++ b/src/rlx_dscv_util.erl @@ -21,7 +21,7 @@ %%% @doc This provider uses the lib_dir setting of the state. It searches the %%% Lib Dirs looking for all OTP Applications that are available. When it finds %%% those OTP Applications it loads the information about them and adds them to -%%% the state of available apps. This implements the rlx_provider behaviour. +%%% the state of available apps. This implements provider behaviour. -module(rlx_dscv_util). -export([do/2, @@ -110,8 +110,8 @@ discover_dir(ProcessDir, File, symlink) -> discover_real_symlink_dir(ProcessDir, File) end. -discover_real_symlink_dir(ProcessDir, File) -> - {ok, ActualRealDir} = file:read_link(File), +discover_real_symlink_dir(ProcessDir, File) -> + {ok, ActualRealDir} = file:read_link(File), case lists:prefix(iolist_to_list(filename:absname(ActualRealDir)), iolist_to_list(filename:absname(File))) of true -> diff --git a/src/rlx_provider.erl b/src/rlx_provider.erl deleted file mode 100644 index b3e6310..0000000 --- a/src/rlx_provider.erl +++ /dev/null @@ -1,117 +0,0 @@ -%% -*- erlang-indent-level: 4; indent-tabs-mode: nil; fill-column: 80 -*- -%%% Copyright 2012 Erlware, LLC. All Rights Reserved. -%%% -%%% This file is provided to you under the Apache License, -%%% Version 2.0 (the "License"); you may not use this file -%%% except in compliance with the License. You may obtain -%%% a copy of the License at -%%% -%%% http://www.apache.org/licenses/LICENSE-2.0 -%%% -%%% Unless required by applicable law or agreed to in writing, -%%% software distributed under the License is distributed on an -%%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -%%% KIND, either express or implied. See the License for the -%%% specific language governing permissions and limitations -%%% under the License. -%%%------------------------------------------------------------------- -%%% @author Eric Merritt -%%% @copyright 2011 Erlware, LLC. -%%% @doc -%%% A module that supports providing state manipulation services to the system. -%%% @end -%%%------------------------------------------------------------------- --module(rlx_provider). - -%% API --export([new/2, - do/2, - impl/1, - format_error/1, - format_error/2, - format/1]). - --export_type([t/0]). - --include("relx.hrl"). - -%%%=================================================================== -%%% Types -%%%=================================================================== - --opaque t() :: {?MODULE, module()}. - - --ifdef(have_callback_support). - --callback init(rlx_state:t()) -> {ok, rlx_state:t()} | relx:error(). --callback do(rlx_state:t()) -> {ok, rlx_state:t()} | relx:error(). --callback format_error(Reason::term()) -> iolist(). - --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}, - {format_error, 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(), rlx_state:t()) -> - {t(), {ok, rlx_state:t()}} | relx:error(). -new(ModuleName, State0) when is_atom(ModuleName) -> - State1 = ModuleName:init(State0), - case code:which(ModuleName) of - non_existing -> - ?RLX_ERROR({non_existing, ModuleName}); - _ -> - {{?MODULE, ModuleName}, State1} - 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(), rlx_state:t()) -> - {ok, rlx_state:t()} | relx:error(). -do({?MODULE, Mod}, State) -> - Mod:do(State). - -%%% @doc get the name of the module that implements the provider -%%% @param Provider the provider object --spec impl(Provider::t()) -> module(). -impl({?MODULE, Mod}) -> - Mod. - -%% @doc format an error produced from a provider. --spec format_error(Reason::term()) -> iolist(). -format_error({non_existing, ModuleName}) -> - io_lib:format("~p does not exist in the system", [ModuleName]). - -%% @doc format an error produced from a provider. --spec format_error(t(), Reason::term()) -> iolist(). -format_error({?MODULE, Mod}, Error) -> - Mod:format_error(Error). - -%% @doc print the provider module name -%% -%% @param T - The provider -%% @return An iolist describing the provider --spec format(t()) -> iolist(). -format({?MODULE, Mod}) -> - erlang:atom_to_list(Mod). diff --git a/src/rlx_prv_archive.erl b/src/rlx_prv_archive.erl index ab640ed..8370659 100644 --- a/src/rlx_prv_archive.erl +++ b/src/rlx_prv_archive.erl @@ -22,7 +22,7 @@ %%% into a release directory. -module(rlx_prv_archive). --behaviour(rlx_provider). +-behaviour(provider). -export([init/1, do/1, @@ -30,12 +30,25 @@ -include("relx.hrl"). +-define(PROVIDER, tar). +-define(DEPS, [release]). + %%============================================================================ %% API %%============================================================================ + -spec init(rlx_state:t()) -> {ok, rlx_state:t()}. init(State) -> - {ok, State}. + State1 = rlx_state:add_provider(State, providers:create([{name, ?PROVIDER}, + {module, ?MODULE}, + {bare, false}, + {deps, ?DEPS}, + {example, "tar"}, + {short_desc, ""}, + {desc, ""}, + {opts, []}])), + + {ok, State1}. -spec do(rlx_state:t()) -> {ok, rlx_state:t()} | relx:error(). do(State) -> diff --git a/src/rlx_prv_assembler.erl b/src/rlx_prv_assembler.erl index f34dfa6..8a25cdd 100644 --- a/src/rlx_prv_assembler.erl +++ b/src/rlx_prv_assembler.erl @@ -22,7 +22,7 @@ %%% into a release directory. -module(rlx_prv_assembler). --behaviour(rlx_provider). +-behaviour(provider). -export([init/1, do/1, @@ -30,31 +30,42 @@ -include("relx.hrl"). +-define(PROVIDER, release). +-define(DEPS, [overlay]). + %%============================================================================ %% API %%============================================================================ -spec init(rlx_state:t()) -> {ok, rlx_state:t()}. init(State) -> - {ok, State}. + State1 = rlx_state:add_provider(State, providers:create([{name, ?PROVIDER}, + {module, ?MODULE}, + {bare, false}, + {deps, ?DEPS}, + {example, "release"}, + {short_desc, ""}, + {desc, ""}, + {opts, []}])), + {ok, State1}. %% @doc recursively dig down into the library directories specified in the state %% looking for OTP Applications -spec do(rlx_state:t()) -> {ok, rlx_state:t()} | relx:error(). -do(State) -> +do(State) -> print_dev_mode(State), {RelName, RelVsn} = rlx_state:default_configured_release(State), Release = rlx_state:get_realized_release(State, RelName, RelVsn), - OutputDir = rlx_state:output_dir(State), + OutputDir = rlx_state:output_dir(State), case create_output_dir(OutputDir) of ok -> case rlx_release:realized(Release) of - true -> + true -> copy_app_directories_to_output(State, Release, OutputDir); false -> ?RLX_ERROR({unresolved_release, RelName, RelVsn}) end; Error -> - Error + Error end. -spec format_error(ErrorDetail::term()) -> iolist(). diff --git a/src/rlx_prv_config.erl b/src/rlx_prv_config.erl deleted file mode 100644 index 5724661..0000000 --- a/src/rlx_prv_config.erl +++ /dev/null @@ -1,278 +0,0 @@ -%% -*- erlang-indent-level: 4; indent-tabs-mode: nil; fill-column: 80 -*- -%%% Copyright 2012 Erlware, LLC. All Rights Reserved. -%%% -%%% This file is provided to you under the Apache License, -%%% Version 2.0 (the "License"); you may not use this file -%%% except in compliance with the License. You may obtain -%%% a copy of the License at -%%% -%%% http://www.apache.org/licenses/LICENSE-2.0 -%%% -%%% Unless required by applicable law or agreed to in writing, -%%% software distributed under the License is distributed on an -%%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -%%% KIND, either express or implied. See the License for the -%%% specific language governing permissions and limitations -%%% under the License. -%%%------------------------------------------------------------------- -%%% @author Eric Merritt -%%% @copyright 2011 Erlware, LLC. -%%% @doc -%%% A module that provides config parsing and support to the system -%%% @end -%%%------------------------------------------------------------------- --module(rlx_prv_config). - --behaviour(rlx_provider). - -%% API --export([init/1, - do/1, - format_error/1]). - --include("relx.hrl"). - -%%%=================================================================== -%%% API -%%%=================================================================== - -%% @doc Required by the system, but not used in this provider --spec init(rlx_state:t()) -> {ok, rlx_state:t()} | relx:error(). -init(State) -> - {ok, State}. - -%% @doc parse all the configs currently specified in the state, -%% populating the state as a result. --spec do(rlx_state:t()) ->{ok, rlx_state:t()} | relx:error(). -do(State) -> - case rlx_state:config_file(State) of - [] -> - search_for_dominating_config(State); - undefined -> - search_for_dominating_config(State); - ConfigFile when erlang:is_list(ConfigFile) -> - load_config(ConfigFile, State) - end. - --spec format_error(Reason::term()) -> iolist(). -format_error({consult, ConfigFile, Reason}) -> - io_lib:format("Unable to read file ~s: ~s", [ConfigFile, - file:format_error(Reason)]); -format_error({invalid_term, Term}) -> - io_lib:format("Invalid term in config file: ~p", [Term]). - -%%%=================================================================== -%%% Internal Functions -%%%=================================================================== -search_for_dominating_config({ok, Cwd}) -> - ConfigFile = filename:join(Cwd, "relx.config"), - case ec_file:exists(ConfigFile) of - true -> - {ok, ConfigFile}; - false -> - search_for_dominating_config(parent_dir(Cwd)) - end; -search_for_dominating_config({error, _}) -> - no_config; -search_for_dominating_config(State0) -> - {ok, Cwd} = file:get_cwd(), - case search_for_dominating_config({ok, Cwd}) of - {ok, Config} -> - %% we need to set the root dir on state as well - {ok, RootDir} = parent_dir(Config), - State1 = rlx_state:root_dir(State0, RootDir), - load_config(Config, rlx_state:config_file(State1, Config)); - no_config -> - {ok, State0} - end. - -%% @doc Given a directory returns the name of the parent directory. --spec parent_dir(Filename::string()) -> - {ok, DirName::string()} | {error, no_parent_dir}. -parent_dir(Filename) -> - parent_dir(filename:split(Filename), []). - -%% @doc Given list of directories, splits the list and returns all dirs but the -%% last as a path. --spec parent_dir([string()], [string()]) -> - {ok, DirName::string()} | {error, no_parent_dir}. -parent_dir([_H], []) -> - {error, no_parent_dir}; -parent_dir([], []) -> - {error, no_parent_dir}; -parent_dir([_H], Acc) -> - {ok, filename:join(lists:reverse(Acc))}; -parent_dir([H | T], Acc) -> - parent_dir(T, [H | Acc]). - --spec load_config(file:filename(), rlx_state:t()) -> - {ok, rlx_state:t()} | relx:error(). -load_config(ConfigFile, State) -> - {ok, CurrentCwd} = file:get_cwd(), - ok = file:set_cwd(filename:dirname(ConfigFile)), - Result = case file:consult(ConfigFile) of - {error, Reason} -> - ?RLX_ERROR({consult, ConfigFile, Reason}); - {ok, Terms} -> - CliTerms = rlx_state:cli_args(State), - lists:foldl(fun load_terms/2, {ok, State}, merge_configs(CliTerms, Terms)) - end, - ok = file:set_cwd(CurrentCwd), - Result. - --spec load_terms(term(), {ok, rlx_state:t()} | relx:error()) -> - {ok, rlx_state:t()} | relx:error(). -load_terms({default_release, {RelName, RelVsn}}, {ok, State}) -> - NewVsn = parse_vsn(RelVsn), - {ok, rlx_state:default_configured_release(State, RelName, NewVsn)}; -load_terms({paths, Paths}, {ok, State}) -> - code:add_pathsa([filename:absname(Path) || Path <- Paths]), - {ok, State}; -load_terms({default_libs, DefaultLibs}, {ok, State}) -> - State2 = rlx_state:put(State, - default_libs, - DefaultLibs), - {ok, State2}; -load_terms({system_libs, SystemLibs}, {ok, State}) -> - State2 = rlx_state:put(State, - system_libs, - SystemLibs), - {ok, State2}; -load_terms({lib_dirs, Dirs}, {ok, State}) -> - State2 = - rlx_state:add_lib_dirs(State, - [list_to_binary(Dir) || Dir <- rlx_util:wildcard_paths(Dirs)]), - {ok, State2}; -load_terms({providers, Providers0}, {ok, State0}) -> - Providers1 = gen_providers(Providers0, State0), - case Providers1 of - {_, E={error, _}} -> - E; - {Providers3, {ok, State3}} -> - {ok, rlx_state:providers(State3, Providers3)} - end; -load_terms({add_providers, Providers0}, {ok, State0}) -> - Providers1 = gen_providers(Providers0, State0), - case Providers1 of - {_, E={error, _}} -> - E; - {Providers3, {ok, State1}} -> - ExistingProviders = rlx_state:providers(State1), - {ok, rlx_state:providers(State1, ExistingProviders ++ Providers3)} - end; -load_terms({skip_apps, SkipApps0}, {ok, State0}) -> - {ok, rlx_state:skip_apps(State0, SkipApps0)}; -load_terms({overrides, Overrides0}, {ok, State0}) -> - {ok, rlx_state:overrides(State0, Overrides0)}; -load_terms({dev_mode, DevMode}, {ok, State0}) -> - {ok, rlx_state:dev_mode(State0, DevMode)}; -load_terms({goals, Goals}, {ok, State0}) -> - {ok, rlx_state:goals(State0, Goals)}; -load_terms({upfrom, UpFrom}, {ok, State0}) -> - {ok, rlx_state:upfrom(State0, UpFrom)}; -load_terms({include_src, IncludeSrc}, {ok, State0}) -> - {ok, rlx_state:include_src(State0, IncludeSrc)}; -load_terms({release, {RelName, Vsn, {extend, RelName2}}, Applications}, {ok, State0}) -> - NewVsn = parse_vsn(Vsn), - Release0 = rlx_release:new(RelName, NewVsn), - ExtendRelease = rlx_state:get_configured_release(State0, RelName2, NewVsn), - Applications1 = rlx_release:goals(ExtendRelease), - case rlx_release:goals(Release0, - lists:umerge(lists:usort(Applications), - lists:usort(Applications1))) of - E={error, _} -> - E; - {ok, Release1} -> - {ok, rlx_state:add_configured_release(State0, Release1)} - end; -load_terms({release, {RelName, Vsn}, Applications}, {ok, State0}) -> - NewVsn = parse_vsn(Vsn), - Release0 = rlx_release:new(RelName, NewVsn), - case rlx_release:goals(Release0, Applications) of - E={error, _} -> - E; - {ok, Release1} -> - {ok, rlx_state:add_configured_release(State0, Release1)} - end; -load_terms({release, {RelName, Vsn}, {erts, ErtsVsn}, - Applications}, {ok, State}) -> - NewVsn = parse_vsn(Vsn), - Release0 = rlx_release:erts(rlx_release:new(RelName, NewVsn), ErtsVsn), - case rlx_release:goals(Release0, Applications) of - E={error, _} -> - E; - {ok, Release1} -> - {ok, rlx_state:add_configured_release(State, Release1)} - end; -load_terms({vm_args, VmArgs}, {ok, State}) -> - {ok, rlx_state:vm_args(State, filename:absname(VmArgs))}; -load_terms({sys_config, SysConfig}, {ok, State}) -> - case rlx_state:sys_config(State) of - undefined -> - {ok, rlx_state:sys_config(State, filename:absname(SysConfig))}; - _ -> - {ok, State} - end; -load_terms({root_dir, Root}, {ok, State}) -> - {ok, rlx_state:root_dir(State, filename:absname(Root))}; -load_terms({output_dir, OutputDir}, {ok, State}) -> - {ok, rlx_state:base_output_dir(State, filename:absname(OutputDir))}; -load_terms({overlay_vars, OverlayVars}, {ok, State}) -> - CurrentOverlayVars = rlx_state:get(State, overlay_vars), - NewOverlayVars0 = list_of_overlay_vars_files(OverlayVars), - NewOverlayVars1 = lists:umerge(lists:usort(NewOverlayVars0), lists:usort(CurrentOverlayVars)), - {ok, rlx_state:put(State, overlay_vars, NewOverlayVars1)}; -load_terms({Name, Value}, {ok, State}) - when erlang:is_atom(Name) -> - {ok, rlx_state:put(State, Name, Value)}; -load_terms(_, Error={error, _}) -> - Error; -load_terms(InvalidTerm, _) -> - ?RLX_ERROR({invalid_term, InvalidTerm}). - --spec gen_providers([module()], rlx_state:t()) -> - {[rlx_provider:t()], {ok, rlx_state:t()} | relx:error()}. -gen_providers(Providers, State) -> - lists:foldl(fun(ProviderName, {Providers1, {ok, State1}}) -> - {Provider, State2} = rlx_provider:new(ProviderName, State1), - {[Provider | Providers1], State2}; - (_, E={_, {error, _}}) -> - E - end, {[], {ok, State}}, Providers). - -list_of_overlay_vars_files(undefined) -> - []; -list_of_overlay_vars_files([]) -> - []; -list_of_overlay_vars_files([H | _]=FileNames) when erlang:is_list(H) -> - FileNames; -list_of_overlay_vars_files(FileName) when is_list(FileName) -> - [FileName]. - -merge_configs([], ConfigTerms) -> - ConfigTerms; -merge_configs([{_Key, undefined} | CliTerms], ConfigTerms) -> - merge_configs(CliTerms, ConfigTerms); -merge_configs([{_Key, []} | CliTerms], ConfigTerms) -> - merge_configs(CliTerms, ConfigTerms); -merge_configs([{Key, Value} | CliTerms], ConfigTerms) -> - case Key of - X when X =:= lib_dirs - ; X =:= goals - ; X =:= overlay_vars - ; X =:= overrides -> - case lists:keyfind(Key, 1, ConfigTerms) of - {Key, Value2} -> - MergedValue = lists:umerge([Value, Value2]), - merge_configs(CliTerms, lists:keyreplace(Key, 1, ConfigTerms, {Key, MergedValue})); - false -> - merge_configs(CliTerms, [{Key, Value} | ConfigTerms]) - end; - _ -> - merge_configs(CliTerms, lists:keystore(Key, 1, ConfigTerms, {Key, Value})) - end. - -parse_vsn(Vsn) when Vsn =:= semver ; Vsn =:= "semver" -> - binary_to_list(ec_git_vsn:vsn([])); -parse_vsn(Vsn) -> - Vsn. diff --git a/src/rlx_prv_discover.erl b/src/rlx_prv_discover.erl index 21e5687..c05cdb1 100644 --- a/src/rlx_prv_discover.erl +++ b/src/rlx_prv_discover.erl @@ -21,9 +21,9 @@ %%% @doc This provider uses the lib_dir setting of the state. It searches the %%% Lib Dirs looking for all OTP Applications that are available. When it finds %%% those OTP Applications it loads the information about them and adds them to -%%% the state of available apps. This implements the rlx_provider behaviour. +%%% the state of available apps. This implements the provider behaviour. -module(rlx_prv_discover). --behaviour(rlx_provider). +-behaviour(provider). -export([init/1, do/1, @@ -31,12 +31,24 @@ -include("relx.hrl"). +-define(PROVIDER, discover). +-define(DEPS, []). + %%============================================================================ %% API %%============================================================================ + -spec init(rlx_state:t()) -> {ok, rlx_state:t()}. init(State) -> - {ok, State}. + State1 = rlx_state:add_provider(State, providers:create([{name, ?PROVIDER}, + {module, ?MODULE}, + {bare, false}, + {deps, ?DEPS}, + {example, "build"}, + {short_desc, ""}, + {desc, ""}, + {opts, []}])), + {ok, State1}. %% @doc recursively dig down into the library directories specified in the state %% looking for OTP Applications diff --git a/src/rlx_prv_overlay.erl b/src/rlx_prv_overlay.erl index 23df218..4cbbf31 100644 --- a/src/rlx_prv_overlay.erl +++ b/src/rlx_prv_overlay.erl @@ -22,7 +22,7 @@ %%% into a release directory. -module(rlx_prv_overlay). --behaviour(rlx_provider). +-behaviour(provider). -export([init/1, do/1, @@ -34,12 +34,24 @@ -include("relx.hrl"). +-define(PROVIDER, overlay). +-define(DEPS, [resolve_release]). + %%============================================================================ %% API %%============================================================================ + -spec init(rlx_state:t()) -> {ok, rlx_state:t()}. init(State) -> - {ok, State}. + State1 = rlx_state:add_provider(State, providers:create([{name, ?PROVIDER}, + {module, ?MODULE}, + {bare, false}, + {deps, ?DEPS}, + {example, "overlay"}, + {short_desc, ""}, + {desc, ""}, + {opts, []}])), + {ok, State1}. %% @doc recursively dig down into the library directories specified in the state %% looking for OTP Applications diff --git a/src/rlx_prv_release.erl b/src/rlx_prv_release.erl index 7e8304b..d579291 100644 --- a/src/rlx_prv_release.erl +++ b/src/rlx_prv_release.erl @@ -21,10 +21,10 @@ %%% @doc This provider uses the lib_dir setting of the state. It searches the %%% Lib Dirs looking for all OTP Applications that are available. When it finds %%% those OTP Applications it loads the information about them and adds them to -%%% the state of available apps. This implements the rlx_provider behaviour. +%%% the state of available apps. This implements the provider behaviour. -module(rlx_prv_release). --behaviour(rlx_provider). +-behaviour(provider). -export([init/1, do/1, @@ -32,12 +32,24 @@ -include("relx.hrl"). +-define(PROVIDER, resolve_release). +-define(DEPS, [discover]). + %%============================================================================ %% API %%============================================================================ + -spec init(rlx_state:t()) -> {ok, rlx_state:t()}. init(State) -> - {ok, State}. + State1 = rlx_state:add_provider(State, providers:create([{name, ?PROVIDER}, + {module, ?MODULE}, + {bare, false}, + {deps, ?DEPS}, + {example, ""}, + {short_desc, ""}, + {desc, ""}, + {opts, []}])), + {ok, State1}. %% @doc recursively dig down into the library directories specified in the state %% looking for OTP Applications diff --git a/src/rlx_prv_relup.erl b/src/rlx_prv_relup.erl index 241d45b..bc86304 100644 --- a/src/rlx_prv_relup.erl +++ b/src/rlx_prv_relup.erl @@ -22,7 +22,7 @@ %%% into a release directory. -module(rlx_prv_relup). --behaviour(rlx_provider). +-behaviour(provider). -export([init/1, do/1, @@ -30,16 +30,28 @@ -include("relx.hrl"). +-define(PROVIDER, relup). +-define(DEPS, [release]). + %%============================================================================ %% API %%============================================================================ + -spec init(rlx_state:t()) -> {ok, rlx_state:t()}. init(State) -> - {ok, State}. + State1 = rlx_state:add_provider(State, providers:create([{name, ?PROVIDER}, + {module, ?MODULE}, + {bare, false}, + {deps, ?DEPS}, + {example, "relup"}, + {short_desc, "Builds release upgrade for latest and last release."}, + {desc, ""}, + {opts, []}])), + {ok, State1}. -spec do(rlx_state:t()) -> {ok, rlx_state:t()} | relx:error(). do(State) -> - {RelName, RelVsn} = rlx_state:default_configured_release(State), + {RelName, RelVsn} = rlx_state:default_configured_release(State), Release0 = rlx_state:get_realized_release(State, RelName, RelVsn), make_relup(State, Release0). @@ -151,4 +163,3 @@ write_relup_file(State, Release, Relup) -> strip_rel(Name) -> rlx_util:to_string(filename:join(filename:dirname(Name), filename:basename(Name, ".rel"))). - diff --git a/src/rlx_rel_discovery.erl b/src/rlx_rel_discovery.erl index 23c0ab9..b7c15bc 100644 --- a/src/rlx_rel_discovery.erl +++ b/src/rlx_rel_discovery.erl @@ -21,7 +21,7 @@ %%% @doc This provider uses the lib_dir setting of the state. It searches the %%% Lib Dirs looking for all OTP Applications that are available. When it finds %%% those OTP Applications it loads the information about them and adds them to -%%% the state of available apps. This implements the rlx_provider behaviour. +%%% the state of available apps. This implements the provider behaviour. -module(rlx_rel_discovery). -export([do/3, diff --git a/src/rlx_state.erl b/src/rlx_state.erl index 267d9fe..71b96f3 100644 --- a/src/rlx_state.erl +++ b/src/rlx_state.erl @@ -44,6 +44,10 @@ cli_args/2, providers/1, providers/2, + add_provider/2, + prepend_hook/3, + append_hook/3, + hooks/2, vm_args/1, vm_args/2, sys_config/1, @@ -91,7 +95,7 @@ config_file=[] :: file:filename() | undefined, cli_args=[] :: proplists:proplist(), goals=[] :: [rlx_depsolver:constraint()], - providers=[] :: [rlx_provider:t()], + providers=[] :: [providers:t()], available_apps=[] :: [rlx_app_info:t()], default_configured_release :: {rlx_release:name() | undefined, rlx_release:vsn() |undefined} | undefined, vm_args :: file:filename() | undefined, @@ -122,11 +126,11 @@ %%============================================================================ %% API %%============================================================================ --spec new(string(), undefined | [atom()]) -> t(). +-spec new(string(), undefined | [atom()]) -> t() | relx:error(). new(Config, Targets) -> new(Config, [], Targets). --spec new(string(), proplists:proplist(), undefined | [atom()]) -> t(). +-spec new(string(), proplists:proplist(), undefined | [atom()]) -> t() | relx:error(). new(Config, CommandLineConfig, undefined) -> new(Config, CommandLineConfig, [release]); new(Config, CommandLineConfig, Targets) @@ -151,6 +155,7 @@ new(Config, CommandLineConfig, Targets) State1 = rlx_state:put(State0, default_libs, true), State2 = rlx_state:put(State1, system_libs, undefined), State3 = rlx_state:put(State2, overlay_vars, []), + create_logic_providers(State3). %% @doc the actions targeted for this system @@ -227,7 +232,7 @@ cli_args(#state_t{cli_args=CliArgs}) -> cli_args(State, CliArgs) -> State#state_t{cli_args=CliArgs}. --spec providers(t()) -> [rlx_provider:t()]. +-spec providers(t()) -> [providers:t()]. providers(#state_t{providers=Providers}) -> Providers. @@ -255,10 +260,14 @@ root_dir(#state_t{root_dir=RootDir}) -> root_dir(State, RootDir) -> State#state_t{root_dir=filename:absname(RootDir)}. --spec providers(t(), [rlx_provider:t()]) -> t(). +-spec providers(t(), [providers:t()]) -> t(). providers(M, NewProviders) -> M#state_t{providers=NewProviders}. +-spec add_provider(t(), providers:t()) -> t(). +add_provider(M=#state_t{providers=Providers}, Provider) -> + M#state_t{providers=[Provider | Providers]}. + -spec add_configured_release(t(), rlx_release:t()) -> t(). add_configured_release(M=#state_t{configured_releases=Releases}, Release) -> M#state_t{configured_releases=ec_dictionary:add({rlx_release:name(Release), @@ -390,55 +399,61 @@ format(#state_t{log=LogState, output_dir=OutDir, lib_dirs=LibDirs, rlx_util:indent(Indent + 2), "lib_dirs: \n", [[rlx_util:indent(Indent + 3), LibDir, ",\n"] || LibDir <- LibDirs], rlx_util:indent(Indent + 2), "providers: \n", - [[rlx_util:indent(Indent + 3), rlx_provider:format(Provider), ",\n"] || Provider <- Providers], + [[rlx_util:indent(Indent + 3), providers:format(Provider), ",\n"] || Provider <- Providers], rlx_util:indent(Indent + 2), "provider config values: \n", [[rlx_util:indent(Indent + 3), io_lib:format("~p", [Value]), ",\n"] || Value <- Values1]]. -%%%=================================================================== -%%% Internal Functions -%%%=================================================================== - --spec create_logic_providers(t()) -> t(). -create_logic_providers(State0) -> - {ConfigProvider, {ok, State1}} = rlx_provider:new(rlx_prv_config, State0), - {DiscoveryProvider, {ok, State2}} = rlx_provider:new(rlx_prv_discover, State1), - {ReleaseProvider, {ok, State3}} = rlx_provider:new(rlx_prv_release, State2), - {OverlayProvider, {ok, State4}} = rlx_provider:new(rlx_prv_overlay, State3), - {ActionProviders, State5} = add_providers([release, relup, tar], State4), - State5#state_t{providers=[ConfigProvider, DiscoveryProvider, - ReleaseProvider, OverlayProvider | ActionProviders]}. - -add_providers(Actions, State) -> - add_providers(Actions, [], State). - -add_providers([], Providers, State) -> - {lists:reverse(Providers), State}; -add_providers([Action | T], Providers, State) -> - case lists:member(Action, actions(State)) of - true -> - {Provider, {ok, State1}} = new_provider(Action, State), - add_providers(T, [Provider | Providers], State1); - false -> - add_providers(T, Providers, State) +prepend_hook(State=#state_t{providers=_Providers}, Target, Hook) -> + {Providers1, State1} = add_hook(pre, Target, Hook, State), + State1#state_t{providers=Providers1}. + +append_hook(State=#state_t{providers=_Providers}, Target, Hook) -> + {Providers1, State1} = add_hook(post, Target, Hook, State), + State1#state_t{providers=Providers1}. + +-spec hooks(t(), atom()) -> {[providers:t()], [providers:t()]}. +hooks(_State=#state_t{providers=Providers}, Target) -> + Provider = providers:get_provider(Target, Providers), + providers:hooks(Provider). + +%% =================================================================== +%% Internal functions +%% =================================================================== + +add_hook(Which, Target, Hook, State) -> + {ok, State1} = providers:new(Hook, State), + Providers1 = providers(State1), + HookProvider = providers:get_provider_by_module(Hook, Providers1), + Provider = providers:get_provider(Target, Providers1), + Hooks = providers:hooks(Provider), + NewHooks = add_hook(Which, Hooks, HookProvider), + NewProvider = providers:hooks(Provider, NewHooks), + {[NewProvider | lists:delete(Provider, Providers1)], State1}. + +add_hook(pre, {PreHooks, PostHooks}, Hook) -> + {[Hook | PreHooks], PostHooks}; +add_hook(post, {PreHooks, PostHooks}, Hook) -> + {PreHooks, [Hook | PostHooks]}. + +-spec create_logic_providers(t()) -> t() | relx:error(). +create_logic_providers(State) -> + create_all(State, [rlx_prv_discover, + rlx_prv_overlay, + rlx_prv_release, + rlx_prv_assembler, + rlx_prv_relup, + rlx_prv_archive]). + +create_all(State, []) -> + State; +create_all(State, [Module | Rest]) -> + case providers:new(Module, State) of + {ok, State1} -> + create_all(State1, Rest); + Error -> + Error end. -new_provider(release, State) -> - rlx_provider:new(rlx_prv_assembler, State); -new_provider(relup, State) -> - rlx_provider:new(rlx_prv_relup, State); -new_provider(tar, State) -> - rlx_provider:new(rlx_prv_archive, State). - %%%=================================================================== %%% Test Functions %%%=================================================================== - --ifndef(NOTEST). --include_lib("eunit/include/eunit.hrl"). - -new_test() -> - LogState = ec_cmd_log:new(error), - RCLState = new("", [{log, LogState}], [release]), - ?assertMatch(LogState, log(RCLState)). - --endif. diff --git a/src/rlx_topo.erl b/src/rlx_topo.erl index b5585df..d24f227 100644 --- a/src/rlx_topo.erl +++ b/src/rlx_topo.erl @@ -32,7 +32,8 @@ %%%------------------------------------------------------------------- -module(rlx_topo). --export([sort_apps/1, +-export([sort/1, + sort_apps/1, format_error/1]). -include("relx.hrl"). @@ -64,6 +65,12 @@ sort_apps(Apps) -> E -> E end. + +%% @doc Do a topological sort on the list of pairs. +-spec sort([pair()]) -> {ok, [atom()]} | relx:error(). +sort(Pairs) -> + iterate(Pairs, [], all(Pairs)). + %% @doc nicely format the error from the sort. -spec format_error(Reason::term()) -> iolist(). format_error({cycle, Pairs}) -> @@ -82,11 +89,6 @@ format_error({cycle, Pairs}) -> %%==================================================================== %% Internal Functions %%==================================================================== -%% @doc Do a topological sort on the list of pairs. --spec sort([pair()]) -> {ok, [atom()]} | relx:error(). -sort(Pairs) -> - iterate(Pairs, [], all(Pairs)). - -spec names_to_apps([atom()], [rlx_app_info:t()]) -> [rlx_app_info:t()]. names_to_apps(Names, Apps) -> [find_app_by_name(Name, Apps) || Name <- Names]. diff --git a/test/rlx_command_SUITE.erl b/test/rlx_command_SUITE.erl index 26783ff..db2794d 100644 --- a/test/rlx_command_SUITE.erl +++ b/test/rlx_command_SUITE.erl @@ -61,19 +61,18 @@ normal_passing_case(Config) -> "-n", RelName, "-v", RelVsn, "-o", Outdir, "-a", "lib1:"++binary_to_list(Lib1)], {ok, {Opts, Targets}} = getopt:parse(relx:opt_spec_list(), CmdLine), {ok, State} = rlx_cmd_args:args2state(Opts, Targets), - {ConfigProvider, {ok, State1}} = rlx_provider:new(rlx_prv_config, State), - {ok, State2} = rlx_provider:do(ConfigProvider, State1), - Overrides = rlx_state:overrides(State2), + {ok, State1} = rlx_config:do(State), + Overrides = rlx_state:overrides(State1), ?assertMatch([{lib1, Lib1}], Overrides), ?assertMatch([Lib1, Lib2], - rlx_state:lib_dirs(State2)), - ?assertMatch(Outdir, rlx_state:base_output_dir(State2)), + rlx_state:lib_dirs(State1)), + ?assertMatch(Outdir, rlx_state:base_output_dir(State1)), ?assertMatch([{app1,{{33,33},{[],[<<"build4">>]}},lte}, {app2, {{33,22},{[],[]}}, {{45,22},{[],[<<"build">>,21]}}, between}], - rlx_state:goals(State2)). + rlx_state:goals(State1)). lib_expansion_case(Config) -> DataDir = proplists:get_value(data_dir, Config), @@ -85,10 +84,9 @@ lib_expansion_case(Config) -> CmdLine = ["-l", filename:join(DataDir, "*")], {ok, {Opts, Targets}} = getopt:parse(relx:opt_spec_list(), CmdLine), {ok, State} = rlx_cmd_args:args2state(Opts, Targets), - {ConfigProvider, {ok, State1}} = rlx_provider:new(rlx_prv_config, State), - {ok, State2} = rlx_provider:do(ConfigProvider, State1), + {ok, State1} = rlx_config:do(State), ?assertMatch([Lib1, Lib2], - rlx_state:lib_dirs(State2)). + rlx_state:lib_dirs(State1)). lib_fail_case(Config) -> DataDir = proplists:get_value(data_dir, Config), diff --git a/test/rlx_discover_SUITE.erl b/test/rlx_discover_SUITE.erl index 7e7015c..ffa64d4 100644 --- a/test/rlx_discover_SUITE.erl +++ b/test/rlx_discover_SUITE.erl @@ -49,11 +49,10 @@ init_per_testcase(_, Config) -> ok = rlx_util:mkdir_p(LibDir1), ok = rlx_util:mkdir_p(LibDir2), State = rlx_state:new([], [{lib_dirs, [LibDir1, LibDir2]}], [release]), - {ConfigProvider, {ok, State1}} = rlx_provider:new(rlx_prv_config, State), - {ok, State2} = rlx_provider:do(ConfigProvider, State1), + {ok, State1} = rlx_config:do(State), [{lib1, LibDir1}, {lib2, LibDir2}, - {state, State2} | Config]. + {state, State1} | Config]. all() -> @@ -78,7 +77,8 @@ normal_case(Config) -> || _ <- lists:seq(1, 100)]], State0 = rlx_state:put(proplists:get_value(state, Config), default_libs, false), - {DiscoverProvider, {ok, State1}} = rlx_provider:new(rlx_prv_discover, State0), + {ok, State1} = rlx_provider:new(rlx_prv_discover, State0), + DiscoverProvider = rlx_provider:get_provider(discover, rlx_state:providers(State1)), {ok, State2} = rlx_provider:do(DiscoverProvider, State1), lists:foreach(fun(App) -> @@ -116,7 +116,8 @@ no_beam_case(Config) -> State0 = proplists:get_value(state, Config), %% Deliberately disable release discovery when running `rlx_prv_discover` State1 = rlx_state:put(State0, disable_rel_discovery, true), - {DiscoverProvider, {ok, State2}} = rlx_provider:new(rlx_prv_discover, State1), + {ok, State2} = rlx_provider:new(rlx_prv_discover, State1), + DiscoverProvider = rlx_provider:get_provider(discover, rlx_state:providers(State2)), ?assertMatch({ok, _}, rlx_provider:do(DiscoverProvider, State2)). @@ -145,7 +146,8 @@ bad_ebin_case(Config) -> ok = filelib:ensure_dir(Filename), ok = ec_file:write_term(Filename, get_bad_app_metadata(BadName, BadVsn)), State0 = proplists:get_value(state, Config), - {DiscoverProvider, {ok, State1}} = rlx_provider:new(rlx_prv_discover, State0), + {ok, State1} = rlx_provider:new(rlx_prv_discover, State0), + DiscoverProvider = rlx_provider:get_provider(discover, rlx_state:providers(State1)), {ok, State2} = rlx_provider:do(DiscoverProvider, State1), ?assertMatch([], [App || App <- rlx_state:available_apps(State2), BadName =:= rlx_app_info:name(App)]). @@ -170,7 +172,8 @@ shallow_app_discovery(Config) -> State0 = rlx_state:put(proplists:get_value(state, Config), default_libs, false), State1 = rlx_state:put(State0, enable_shallow_app_discovery, true), - {DiscoverProvider, {ok, State2}} = rlx_provider:new(rlx_prv_discover, State1), + {ok, State2} = rlx_provider:new(rlx_prv_discover, State1), + DiscoverProvider = rlx_provider:get_provider(discover, rlx_state:providers(State2)), {ok, State3} = rlx_provider:do(DiscoverProvider, State2), lists:foreach(fun(App) -> ?assertMatch(true, lists:member(App, rlx_state:available_apps(State3))) diff --git a/test/rlx_release_SUITE.erl b/test/rlx_release_SUITE.erl index 6f067dc..112afc8 100644 --- a/test/rlx_release_SUITE.erl +++ b/test/rlx_release_SUITE.erl @@ -61,10 +61,9 @@ init_per_testcase(_, Config) -> LibDir1 = filename:join([DataDir, create_random_name("lib_dir1_")]), ok = rlx_util:mkdir_p(LibDir1), State = rlx_state:new([], [{lib_dirs, [LibDir1]}], [release]), - {ConfigProvider, {ok, State1}} = rlx_provider:new(rlx_prv_config, State), - {ok, State2} = rlx_provider:do(ConfigProvider, State1), + {ok, State1} = rlx_config:do(State), [{lib1, LibDir1}, - {state, State2} | Config]. + {state, State1} | Config]. all() -> [make_release, make_extend_release, make_scriptless_release, @@ -178,7 +177,7 @@ make_invalid_config_release(Config) -> goal_app_2,]}"), OutputDir = filename:join([proplists:get_value(data_dir, Config), create_random_name("relx-output")]), - {error, {rlx_prv_config, + {error, {rlx_config, {consult, _, _}}} = relx:do(undefined, undefined, [], [LibDir1], 3, OutputDir, ConfigFile). @@ -575,7 +574,7 @@ overlay_release(Config) -> Error -> erlang:throw({failed_to_consult, Error}) end, - + {ok, ReadFileInfo} = file:read_file_info(filename:join([OutputDir, "foo", "test_template_resolved"])), ?assertEqual(8#100777, ReadFileInfo#file_info.mode), @@ -742,12 +741,12 @@ make_relup_release(Config) -> {ok, _} = relx:do(foo, "0.0.2", [], [LibDir1], 3, OutputDir, ConfigFile), {ok, State} = relx:do([{relname, foo}, - {relvsn, "0.0.3"}, - {goals, []}, - {lib_dirs, [LibDir1]}, - {log_level, 3}, - {output_dir, OutputDir}, - {config, ConfigFile}], ["release", "relup"]), + {relvsn, "0.0.3"}, + {goals, []}, + {lib_dirs, [LibDir1]}, + {log_level, 3}, + {output_dir, OutputDir}, + {config, ConfigFile}], ["release", "relup"]), %% we should have one 'resolved' release and three discovered realized_releases. ?assertMatch([{foo, "0.0.1"}, -- cgit v1.2.3