diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/relx.app.src | 2 | ||||
-rw-r--r-- | src/relx.erl | 63 | ||||
-rw-r--r-- | src/rlx_app_discovery.erl | 2 | ||||
-rw-r--r-- | src/rlx_cmd_args.erl | 27 | ||||
-rw-r--r-- | src/rlx_config.erl (renamed from src/rlx_prv_config.erl) | 53 | ||||
-rw-r--r-- | src/rlx_dscv_util.erl | 6 | ||||
-rw-r--r-- | src/rlx_provider.erl | 117 | ||||
-rw-r--r-- | src/rlx_prv_archive.erl | 17 | ||||
-rw-r--r-- | src/rlx_prv_assembler.erl | 23 | ||||
-rw-r--r-- | src/rlx_prv_discover.erl | 21 | ||||
-rw-r--r-- | src/rlx_prv_overlay.erl | 23 | ||||
-rw-r--r-- | src/rlx_prv_release.erl | 18 | ||||
-rw-r--r-- | src/rlx_prv_relup.erl | 19 | ||||
-rw-r--r-- | src/rlx_rel_discovery.erl | 2 | ||||
-rw-r--r-- | src/rlx_state.erl | 117 | ||||
-rw-r--r-- | src/rlx_topo.erl | 14 |
16 files changed, 263 insertions, 261 deletions
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..37918be 100644 --- a/src/relx.erl +++ b/src/relx.erl @@ -21,6 +21,7 @@ -module(relx). -export([main/1, + main/2, do/2, do/7, do/8, @@ -44,6 +45,9 @@ %%============================================================================ -spec main([string()]) -> ok | error() | {ok, rlx_state:t()}. main(Args) -> + main([], Args). + +main(ApiOptions, Args) -> OptSpecList = opt_spec_list(), Result = case getopt:parse(OptSpecList, Args) of {ok, {Options, NonOptions}} -> @@ -57,7 +61,8 @@ main(Args) -> true -> usage(); false -> - do([{caller, command_line} | Options], NonOptions) + application:start(relx), + do(ApiOptions++[{caller, command_line} | Options], NonOptions) end end; {error, Detail} -> @@ -212,7 +217,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 +245,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 +276,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..ac3e718 100644 --- a/src/rlx_cmd_args.erl +++ b/src/rlx_cmd_args.erl @@ -69,7 +69,7 @@ format_error({invalid_option_arg, Arg}) -> io_lib:format("Invalid code path argument -n ~p~n", [Path]) end; format_error({invalid_config_file, Config}) -> - io_lib:format("Invalid configuration file specified: ~s", [Config]); + io_lib:format("Invalid configuration file specified: ~p", [Config]); format_error({invalid_caller, Caller}) -> io_lib:format("Invalid caller specified: ~s", [Caller]); format_error({failed_to_parse, Spec}) -> @@ -87,15 +87,19 @@ 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)} + case rlx_state:new(Config, CommandLineConfig, Targets) of + {error, Error} -> + {error, Error}; + State -> + {ok, State} + end end. -spec convert_targets([string()]) -> {ok, release | relup} | relx:error(). @@ -117,8 +121,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} | relx:error(). validate_config(undefined) -> {ok, undefined}; validate_config("") -> @@ -128,7 +132,14 @@ validate_config(Config) -> true -> {ok, filename:absname(Config)}; false -> - ?RLX_ERROR({invalid_config_file, Config}) + case io_lib:printable_list(Config) of + true -> + ?RLX_ERROR({invalid_config_file, Config}); + false when is_list(Config) -> + {ok, Config}; + false -> + ?RLX_ERROR({invalid_config_file, Config}) + end end. run_creates(Opts) -> diff --git a/src/rlx_prv_config.erl b/src/rlx_config.erl index 5724661..dbfe1c6 100644 --- a/src/rlx_prv_config.erl +++ b/src/rlx_config.erl @@ -21,13 +21,10 @@ %%% A module that provides config parsing and support to the system %%% @end %%%------------------------------------------------------------------- --module(rlx_prv_config). - --behaviour(rlx_provider). +-module(rlx_config). %% API --export([init/1, - do/1, +-export([do/1, format_error/1]). -include("relx.hrl"). @@ -36,11 +33,6 @@ %%% 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(). @@ -105,20 +97,26 @@ parent_dir([_H], Acc) -> parent_dir([H | T], Acc) -> parent_dir(T, [H | Acc]). --spec load_config(file:filename(), rlx_state:t()) -> +-spec load_config(file:filename() | proplists:proplist(), 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. + case filelib:is_regular(ConfigFile) of + true -> + 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; + false -> + CliTerms = rlx_state:cli_args(State), + lists:foldl(fun load_terms/2, {ok, State}, merge_configs(CliTerms, ConfigFile)) + end. -spec load_terms(term(), {ok, rlx_state:t()} | relx:error()) -> {ok, rlx_state:t()} | relx:error(). @@ -143,6 +141,8 @@ load_terms({lib_dirs, Dirs}, {ok, State}) -> 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 @@ -231,15 +231,22 @@ load_terms(InvalidTerm, _) -> ?RLX_ERROR({invalid_term, InvalidTerm}). -spec gen_providers([module()], rlx_state:t()) -> - {[rlx_provider:t()], {ok, rlx_state:t()} | relx:error()}. + {[providers: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, 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([]) -> 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 <[email protected]> -%%% @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_discover.erl b/src/rlx_prv_discover.erl index 21e5687..520cf15 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 @@ -97,6 +109,7 @@ add_common_project_dirs(State) -> Apps = filename:join(Root, "apps"), Lib = filename:join(Root, "lib"), Deps = filename:join(Root, "deps"), + Rebar3Deps = filename:join(Root, "_deps"), Ebin = filename:join(Root, "ebin"), lists:foldl(fun(Dir, LibDirs) -> case ec_file:exists(Dir) of @@ -105,7 +118,7 @@ add_common_project_dirs(State) -> false -> LibDirs end - end, [], [Deps, Lib, Apps, Ebin]) + end, [], [Rebar3Deps, Deps, Lib, Apps, Ebin]) end. -spec add_system_lib_dir(rlx_state:t()) -> [file:name()]. diff --git a/src/rlx_prv_overlay.erl b/src/rlx_prv_overlay.erl index 23df218..f7e2be8 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 @@ -365,7 +377,12 @@ get_relative_root(State) -> [] -> rlx_state:root_dir(State); Config -> - filename:dirname(Config) + case filelib:is_regular(Config) of + true -> + filename:dirname(Config); + false -> + rlx_state:root_dir(State) + end end. -spec is_directory(file:name(), file:name()) -> boolean(). 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..a25c60f 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 @@ -211,11 +216,11 @@ goals(#state_t{goals=TS}) -> goals(State, Goals) -> State#state_t{goals=Goals}. --spec config_file(t()) -> file:filename() | undefined. +-spec config_file(t()) -> file:filename() | proplists:proplist() | undefined. config_file(#state_t{config_file=ConfigFiles}) -> ConfigFiles. --spec config_file(t(), file:filename() | undefined) -> t(). +-spec config_file(t(), file:filename() | proplists:proplist() | undefined) -> t(). config_file(State, ConfigFiles) -> State#state_t{config_file=ConfigFiles}. @@ -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]. |