From 7d3626779ce5716055b31eed76feb81a9fcb2210 Mon Sep 17 00:00:00 2001 From: Tristan Sloughter Date: Wed, 7 May 2014 19:32:17 -0500 Subject: refactor cli args and config file var merging --- src/rlx_cmd_args.erl | 253 ++++++++++++++++++++---------------------- src/rlx_prv_assembler.erl | 2 +- src/rlx_prv_config.erl | 56 ++++++++-- src/rlx_state.erl | 101 +++++++++-------- test/rlx_command_SUITE.erl | 12 +- test/rlx_depsolver_tester.erl | 1 - test/rlx_discover_SUITE.erl | 7 +- test/rlx_release_SUITE.erl | 14 ++- 8 files changed, 247 insertions(+), 199 deletions(-) diff --git a/src/rlx_cmd_args.erl b/src/rlx_cmd_args.erl index da8f3a0..9233db9 100644 --- a/src/rlx_cmd_args.erl +++ b/src/rlx_cmd_args.erl @@ -33,16 +33,16 @@ {ok, {rlx_state:t(), [string()]}} | relx:error(). args2state(Opts, Targets) -> - RelName = rlx_util:to_atom(proplists:get_value(relname, Opts, undefined)), - RelVsn = proplists:get_value(relvsn, Opts, undefined), case convert_targets(Targets) of {ok, AtomizedTargets} -> - case create_log(Opts, [{relname, RelName}, - {relvsn, RelVsn}]) of + case run_creates(Opts) of Error = {error, _} -> Error; {ok, CommandLineConfig} -> - handle_config(Opts, AtomizedTargets, CommandLineConfig) + RelName = rlx_util:to_atom(proplists:get_value(relname, Opts, undefined)), + RelVsn = proplists:get_value(relvsn, Opts, undefined), + handle_config(Opts, AtomizedTargets, + [{default_release, {RelName, RelVsn}} | CommandLineConfig]) end; Error -> Error @@ -95,7 +95,7 @@ handle_config(Opts, Targets, CommandLineConfig) -> Error = {error, _} -> Error; {ok, Config} -> - {ok, rlx_state:new([{config, Config} | CommandLineConfig], Targets)} + {ok, rlx_state:new(Config, CommandLineConfig, Targets)} end. -spec convert_targets([string()]) -> {ok, release | relup} | relx:error(). @@ -131,40 +131,137 @@ validate_config(Config) -> ?RLX_ERROR({invalid_config_file, Config}) end. --spec create_log([getopt:option()], rlx_state:cmd_args()) -> - {ok, rlx_state:cmd_args()} | relx:error(). -create_log(Opts, Acc) -> +run_creates(Opts) -> + try + Conf = lists:flatten(lists:foldl(fun(X, Acc) -> + [create(X, Opts) | Acc] + end, [], proplists:get_keys(Opts))), + {ok, Conf} + catch + throw:E -> + E + end. + +create(log_level, Opts) -> LogLevel = proplists:get_value(log_level, Opts, 0), if LogLevel >= 0, LogLevel =< 3 -> - create_goals(Opts, [{log, ec_cmd_log:new(LogLevel, command_line)} | Acc]); + {log, ec_cmd_log:new(LogLevel, command_line)}; true -> - ?RLX_ERROR({invalid_log_level, LogLevel}) - end. - --spec create_goals([getopt:option()], rlx_state:cmd_args()) -> - {ok, rlx_state:cmd_args()} | relx:error(). -create_goals(Opts, Acc) -> + throw(?RLX_ERROR({invalid_log_level, LogLevel})) + end; +create(goal, Opts) -> Goals = proplists:get_value(goals, Opts, []) ++ proplists:get_all_values(goal, Opts), case convert_goals(Goals, []) of Error={error, _} -> - Error; + throw(Error); {ok, Specs} -> - create_overrides(Opts, [{goals, Specs} | Acc]) - end. - --spec create_overrides([getopt:option()], rlx_state:cmd_args()) -> - {ok, rlx_state:cmd_args()} | relx:error(). -create_overrides(Opts, Acc) -> + {goals, Specs} + end; +create(goals, Opts) -> + Goals = proplists:get_value(goals, Opts, []) ++ + proplists:get_all_values(goal, Opts), + case convert_goals(Goals, []) of + Error={error, _} -> + throw(Error); + {ok, Specs} -> + {goals, Specs} + end; +create(overrides, Opts) -> Overrides = proplists:get_all_values(override, Opts) ++ proplists:get_value(overrides, Opts, []), case convert_overrides(Overrides, []) of {ok, Overrides} -> - create_output_dir(Opts, [{overrides, Overrides} | Acc]); + {overrides, Overrides}; Error -> - Error - end. + throw(Error) + end; +create(output_dir, Opts) -> + OutputDir = proplists:get_value(output_dir, Opts, "./_rel"), + {output_dir, filename:absname(OutputDir)}; +create(lib_dir, Opts) -> + Dirs = proplists:get_all_values(lib_dir, Opts) ++ + proplists:get_value(lib_dirs, Opts, []), + ExpDirs = rlx_util:wildcard_paths(Dirs), + case check_lib_dirs(ExpDirs) of + Error = {error, _} -> + throw(Error); + ok -> + LibDirs = [rlx_util:to_binary(Dir) || Dir <- ExpDirs], + {lib_dirs, LibDirs} + end; +create(lib_dirs, Opts) -> + Dirs = proplists:get_all_values(lib_dir, Opts) ++ + proplists:get_value(lib_dirs, Opts, []), + ExpDirs = rlx_util:wildcard_paths(Dirs), + case check_lib_dirs(ExpDirs) of + Error = {error, _} -> + throw(Error); + ok -> + LibDirs = [rlx_util:to_binary(Dir) || Dir <- ExpDirs], + {lib_dirs, LibDirs} + end; +create(root_dir, Opts) -> + Dir = proplists:get_value(root_dir, Opts, undefined), + case Dir of + undefined -> + {ok, Cwd} = file:get_cwd(), + {root_dir, Cwd}; + _ -> + {root_dir, filename:absname(Dir)} + end; +create(default_libs, Opts) -> + Def = proplists:get_value(default_libs, Opts, true), + {default_libs, Def}; +create(overlay_vars, Opts)-> + OverlayVars = proplists:get_all_values(overlay_vars, Opts), + {overlay_vars, OverlayVars}; +create(sys_config, Opts) -> + SysConfig = proplists:get_value(sys_config, Opts, undefined), + {sys_config, SysConfig}; +create(system_libs, Opts) -> + SystemLibs = proplists:get_value(system_libs, Opts, undefined), + {system_libs, SystemLibs}; +create(upfrom, Opts) -> + case proplists:get_value(upfrom, Opts, undefined) of + undefined -> + []; + UpFrom -> + {upfrom, UpFrom} + end; +create(caller, Opts) -> + case proplists:get_value(caller, Opts, api) of + "command_line" -> + {caller, command_line}; + "commandline" -> + {caller, command_line}; + "api" -> + {caller, api}; + api -> + {caller, api}; + commandline -> + {caller, command_line}; + command_line -> + {caller, command_line}; + Caller -> + ?RLX_ERROR({invalid_caller, Caller}) + end; +create(paths, Opts) -> + Dirs = proplists:get_all_values(path, Opts) ++ + proplists:get_value(paths, Opts, []), + case check_lib_dirs(Dirs) of + Error = {error, _} -> + throw(Error); + ok -> + code:add_pathsa([filename:absname(Path) || Path <- Dirs]), + [] + end; +create(dev_mode, Opts) -> + DevMode = proplists:get_value(dev_mode, Opts, false), + {dev_mode, DevMode}; +create(_, _) -> + []. -spec convert_overrides([{atom(), string() | binary()} | string() | binary()], [{atom(), string() | binary()}]) -> @@ -212,110 +309,6 @@ parse_goal(RawSpec, Rest, Acc) -> ?RLX_ERROR({failed_to_parse, RawSpec}) end. --spec create_output_dir([getopt:option()], rlx_state:cmd_args()) -> - {ok, rlx_state:cmd_args()} | relx:error(). -create_output_dir(Opts, Acc) -> - OutputDir = proplists:get_value(output_dir, Opts, "./_rel"), - create_lib_dirs(Opts, [{output_dir, filename:absname(OutputDir)} | Acc]). - --spec create_lib_dirs([getopt:option()], rlx_state:cmd_args()) -> - {ok, rlx_state:cmd_args()} | relx:error(). -create_lib_dirs(Opts, Acc) -> - Dirs = proplists:get_all_values(lib_dir, Opts) ++ - proplists:get_value(lib_dirs, Opts, []), - ExpDirs = rlx_util:wildcard_paths(Dirs), - case check_lib_dirs(ExpDirs) of - Error = {error, _} -> - Error; - ok -> - create_root_dir(Opts, [{lib_dirs, ExpDirs} | Acc]) - end. - --spec create_root_dir([getopt:option()], rlx_state:cmd_args()) -> - {ok, rlx_state:cmd_args()} | relx:error(). -create_root_dir(Opts, Acc) -> - Dir = proplists:get_value(root_dir, Opts, undefined), - case Dir of - undefined -> - {ok, Cwd} = file:get_cwd(), - create_disable_default_libs(Opts, [{root_dir, Cwd} | Acc]); - _ -> - create_disable_default_libs(Opts, [{root_dir, Dir} | Acc]) - end. - --spec create_disable_default_libs([getopt:option()], rlx_state:cmd_args()) -> - {ok, rlx_state:cmd_args()} | relx:error(). -create_disable_default_libs(Opts, Acc) -> - Def = proplists:get_value(default_libs, Opts, true), - create_overlay_vars(Opts, [{default_libs, Def} | Acc]). - --spec create_overlay_vars([getopt:option()], rlx_state:cmd_args()) -> - {ok, rlx_state:cmd_args()} | relx:error(). -create_overlay_vars(Opts, Acc) -> - OverlayVars = proplists:get_all_values(overlay_vars, Opts), - create_sys_config(Opts, [{overlay_vars, OverlayVars} | Acc]). - --spec create_sys_config([getopt:option()], rlx_state:cmd_args()) -> - {ok, rlx_state:cmd_args()} | relx:error(). -create_sys_config(Opts, Acc) -> - SysConfig = proplists:get_value(sys_config, Opts, undefined), - create_system_libs(Opts, [{sys_config, SysConfig} | Acc]). - --spec create_system_libs([getopt:option()], rlx_state:cmd_args()) -> - {ok, rlx_state:cmd_args()} | relx:error(). -create_system_libs(Opts, Acc) -> - SystemLibs = proplists:get_value(system_libs, Opts, undefined), - create_upfrom(Opts, [{system_libs, SystemLibs} | Acc]). - --spec create_upfrom([getopt:option()], rlx_state:cmd_args()) -> - {ok, rlx_state:cmd_args()} | relx:error(). -create_upfrom(Opts, Acc) -> - case proplists:get_value(upfrom, Opts, undefined) of - undefined -> - create_caller(Opts, Acc); - UpFrom -> - create_caller(Opts, [{upfrom, UpFrom} | Acc]) - end. - --spec create_caller([getopt:option()], rlx_state:cmd_args()) -> - {ok, rlx_state:cmd_args()} | relx:error(). -create_caller(Opts, Acc) -> - case proplists:get_value(caller, Opts, api) of - "command_line" -> - create_paths(Opts, [{caller, command_line} | Acc]); - "commandline" -> - create_paths(Opts, [{caller, command_line} | Acc]); - "api" -> - create_paths(Opts, [{caller, api} | Acc]); - api -> - create_paths(Opts, [{caller, api} | Acc]); - commandline -> - create_paths(Opts, [{caller, command_line} | Acc]); - command_line -> - create_paths(Opts, [{caller, command_line} | Acc]); - Caller -> - ?RLX_ERROR({invalid_caller, Caller}) - end. - --spec create_paths([getopt:option()], rlx_state:cmd_args()) -> - {ok, rlx_state:cmd_args()} | relx:error(). -create_paths(Opts, Acc) -> - Dirs = proplists:get_all_values(path, Opts) ++ - proplists:get_value(paths, Opts, []), - case check_lib_dirs(Dirs) of - Error = {error, _} -> - Error; - ok -> - code:add_pathsa([filename:absname(Path) || Path <- Dirs]), - create_dev_mode(Opts, Acc) - end. - --spec create_dev_mode([getopt:option()], rlx_state:cmd_args()) -> - {ok, rlx_state:cmd_args()} | relx:error(). -create_dev_mode(Opts, Acc) -> - DevMode = proplists:get_value(dev_mode, Opts, false), - {ok, [{dev_mode, DevMode} | Acc]}. - -spec check_lib_dirs([string()]) -> ok | relx:error(). check_lib_dirs([]) -> ok; diff --git a/src/rlx_prv_assembler.erl b/src/rlx_prv_assembler.erl index 47d96aa..1afd18d 100644 --- a/src/rlx_prv_assembler.erl +++ b/src/rlx_prv_assembler.erl @@ -605,7 +605,7 @@ make_upfrom_script(State, Release, UpFrom) -> case make_script(Options, fun(CorrectOptions) -> systools:make_relup(CurrentRel, [UpFromRel], [UpFromRel], CorrectOptions) - end) of + end) of ok -> ec_cmd_log:error(rlx_state:log(State), "relup from ~s to ~s successfully created!", diff --git a/src/rlx_prv_config.erl b/src/rlx_prv_config.erl index 9efd8a0..dd7dc70 100644 --- a/src/rlx_prv_config.erl +++ b/src/rlx_prv_config.erl @@ -114,23 +114,34 @@ load_config(ConfigFile, State) -> {error, Reason} -> ?RLX_ERROR({consult, ConfigFile, Reason}); {ok, Terms} -> - lists:foldl(fun load_terms/2, {ok, State}, 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}) -> - case rlx_state:default_configured_release(State) of - {undefined, undefined} -> - {ok, rlx_state:default_configured_release(State, RelName, RelVsn)}; - _ -> - {ok, State} - end; +load_terms({default_release, {RelName, RelVsn}}, {ok, State}) -> + {ok, rlx_state:default_configured_release(State, RelName, RelVsn)}; 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({overlay_vars, OverlayVars}, {ok, State}) -> + State2 = rlx_state:put(State, + overlay_vars, + list_of_overlay_vars_files(OverlayVars)), + {ok, State2}; load_terms({lib_dirs, Dirs}, {ok, State}) -> State2 = rlx_state:add_lib_dirs(State, @@ -159,6 +170,10 @@ 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}) -> @@ -199,6 +214,8 @@ load_terms({sys_config, SysConfig}, {ok, State}) -> _ -> {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}) -> @@ -232,3 +249,26 @@ 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, [{Key, Value} | ConfigTerms]) + end. diff --git a/src/rlx_state.erl b/src/rlx_state.erl index 5b488c8..c84be78 100644 --- a/src/rlx_state.erl +++ b/src/rlx_state.erl @@ -24,6 +24,7 @@ -module(rlx_state). -export([new/2, + new/3, log/1, actions/1, output_dir/1, @@ -36,8 +37,11 @@ skip_apps/1, skip_apps/2, goals/1, + goals/2, config_file/1, config_file/2, + cli_args/1, + cli_args/2, providers/1, providers/2, vm_args/1, @@ -68,6 +72,7 @@ include_src/1, include_src/2, upfrom/1, + upfrom/2, format/1, format/2]). @@ -84,13 +89,14 @@ output_dir :: file:name(), lib_dirs=[] :: [file:name()], config_file=[] :: file:filename() | undefined, + cli_args=[] :: proplists:proplist(), goals=[] :: [rlx_depsolver:constraint()], providers=[] :: [rlx_provider:t()], available_apps=[] :: [rlx_app_info:t()], - default_configured_release :: {rlx_release:name(), rlx_release:vsn()}, + default_configured_release :: {rlx_release:name(), rlx_release:vsn()} | undefined, vm_args :: file:filename() | undefined, sys_config :: file:filename() | undefined, - overrides :: [{AppName::atom(), Directory::file:filename()}], + overrides=[] :: [{AppName::atom(), Directory::file:filename()}], skip_apps=[] :: [AppName::atom()], configured_releases :: releases(), realized_releases :: releases(), @@ -116,45 +122,37 @@ %%============================================================================ %% API %%============================================================================ -%% @doc Create a new 'log level' for the system --spec new(proplists:proplist(), undefined | [atom()]) -> t(). -new(PropList, undefined) -> - new(PropList, [release]); -new(PropList, Targets) - when erlang:is_list(PropList), +-spec new(string(), undefined | [atom()]) -> t(). +new(Config, Targets) -> + new(Config, [], Targets). + +-spec new(string(), proplists:proplist(), undefined | [atom()]) -> t(). +new(Config, CommandLineConfig, undefined) -> + new(Config, CommandLineConfig, [release]); +new(Config, CommandLineConfig, Targets) + when erlang:is_list(CommandLineConfig), erlang:is_list(Targets) -> {ok, Root} = file:get_cwd(), - Caller = proplists:get_value(caller, PropList, api), - State0 = - #state_t{log = proplists:get_value(log, PropList, ec_cmd_log:new(error, Caller)), - output_dir=proplists:get_value(output_dir, PropList, ""), - lib_dirs=[to_binary(Dir) || Dir <- proplists:get_value(lib_dirs, PropList, [])], - config_file=proplists:get_value(config, PropList, undefined), - sys_config=proplists:get_value(sys_config, PropList, undefined), - dev_mode = proplists:get_value(dev_mode, PropList), - actions = Targets, - caller = Caller, - goals=proplists:get_value(goals, PropList, []), - providers = [], - configured_releases=ec_dictionary:new(ec_dict), - realized_releases=ec_dictionary:new(ec_dict), - config_values=ec_dictionary:new(ec_dict), - overrides = proplists:get_value(overrides, PropList, []), - root_dir = filename:absname(proplists:get_value(root_dir, PropList, Root)), - upfrom = proplists:get_value(upfrom, PropList, undefined), - default_configured_release={proplists:get_value(relname, PropList, undefined), - proplists:get_value(relvsn, PropList, undefined)}}, - State1 = rlx_state:put(create_logic_providers(State0), - default_libs, - proplists:get_value(default_libs, PropList, true)), - - State2 = rlx_state:put(create_logic_providers(State1), - system_libs, - proplists:get_value(system_libs, PropList, undefined)), - - rlx_state:put(create_logic_providers(State2), - overlay_vars, - proplists:get_value(overlay_vars, PropList, [])). + + Caller = proplists:get_value(caller, CommandLineConfig, api), + Log = proplists:get_value(log, CommandLineConfig, ec_cmd_log:new(error, Caller)), + + State0 = #state_t{log=Log, + config_file=Config, + cli_args=CommandLineConfig, + actions=Targets, + caller=Caller, + root_dir=Root, + output_dir=filename:join(Root, "_rel"), + providers=[], + default_configured_release=undefined, + configured_releases=ec_dictionary:new(ec_dict), + realized_releases=ec_dictionary:new(ec_dict), + config_values=ec_dictionary:new(ec_dict)}, + 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 -spec actions(t()) -> [action()]. @@ -210,6 +208,10 @@ add_lib_dirs(State=#state_t{lib_dirs=LibDir}, Dirs) -> goals(#state_t{goals=TS}) -> TS. +-spec goals(t(), [rlx_depsolver:constraint()]) -> t(). +goals(State, Goals) -> + State#state_t{goals=Goals}. + -spec config_file(t()) -> file:filename() | undefined. config_file(#state_t{config_file=ConfigFiles}) -> ConfigFiles. @@ -218,6 +220,14 @@ config_file(#state_t{config_file=ConfigFiles}) -> config_file(State, ConfigFiles) -> State#state_t{config_file=ConfigFiles}. +-spec cli_args(t()) -> proplists:proplist(). +cli_args(#state_t{cli_args=CliArgs}) -> + CliArgs. + +-spec cli_args(t(), proplists:proplist()) -> t(). +cli_args(State, CliArgs) -> + State#state_t{cli_args=CliArgs}. + -spec providers(t()) -> [rlx_provider:t()]. providers(#state_t{providers=Providers}) -> Providers. @@ -356,6 +366,10 @@ include_src(S, IncludeSrc) -> upfrom(#state_t{upfrom=UpFrom}) -> UpFrom. +-spec upfrom(t(), string() | binary() | undefined) -> t(). +upfrom(State, UpFrom) -> + State#state_t{upfrom=UpFrom}. + -spec format(t()) -> iolist(). format(Mod) -> format(Mod, 0). @@ -395,13 +409,6 @@ create_logic_providers(State0) -> State5#state_t{providers=[ConfigProvider, DiscoveryProvider, ReleaseProvider, OverlayProvider, AssemblerProvider]}. -to_binary(Dir) - when erlang:is_list(Dir) -> - erlang:list_to_binary(Dir); -to_binary(Dir) - when erlang:is_binary(Dir) -> - Dir. - %%%=================================================================== %%% Test Functions %%%=================================================================== @@ -411,7 +418,7 @@ to_binary(Dir) new_test() -> LogState = ec_cmd_log:new(error), - RCLState = new([{log, LogState}], [release]), + RCLState = new(LogState, [], [release]), ?assertMatch(LogState, log(RCLState)). -endif. diff --git a/test/rlx_command_SUITE.erl b/test/rlx_command_SUITE.erl index 2e1ef1d..e26e0ad 100644 --- a/test/rlx_command_SUITE.erl +++ b/test/rlx_command_SUITE.erl @@ -61,15 +61,17 @@ normal_passing_case(Config) -> "-n", RelName, "-v", RelVsn, "-o", Outdir], {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), ?assertMatch([Lib1, Lib2], - rlx_state:lib_dirs(State)), - ?assertMatch(Outdir, rlx_state:base_output_dir(State)), + rlx_state:lib_dirs(State2)), + ?assertMatch(Outdir, rlx_state:output_dir(State2)), ?assertMatch([{app1,{{33,33},{[],[<<"build4">>]}},lte}, {app2, {{33,22},{[],[]}}, {{45,22},{[],[<<"build">>,21]}}, between}], - rlx_state:goals(State)). + rlx_state:goals(State2)). lib_expansion_case(Config) -> DataDir = proplists:get_value(data_dir, Config), @@ -81,8 +83,10 @@ 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), ?assertMatch([Lib1, Lib2], - rlx_state:lib_dirs(State)). + rlx_state:lib_dirs(State2)). lib_fail_case(Config) -> DataDir = proplists:get_value(data_dir, Config), diff --git a/test/rlx_depsolver_tester.erl b/test/rlx_depsolver_tester.erl index 2dddf76..b441a1e 100644 --- a/test/rlx_depsolver_tester.erl +++ b/test/rlx_depsolver_tester.erl @@ -365,7 +365,6 @@ fix_rebar_brokenness(Filename) -> true -> Alt2; false -> - io:format("~p~n", [Alt2]), erlang:throw(unable_to_find_data_files) end end. diff --git a/test/rlx_discover_SUITE.erl b/test/rlx_discover_SUITE.erl index 8ea95c4..7e7015c 100644 --- a/test/rlx_discover_SUITE.erl +++ b/test/rlx_discover_SUITE.erl @@ -48,10 +48,12 @@ init_per_testcase(_, Config) -> LibDir2 = filename:join([DataDir, create_random_name("lib_dir2_")]), ok = rlx_util:mkdir_p(LibDir1), ok = rlx_util:mkdir_p(LibDir2), - State = rlx_state:new([{lib_dirs, [LibDir1, LibDir2]}], [release]), + 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), [{lib1, LibDir1}, {lib2, LibDir2}, - {state, State} | Config]. + {state, State2} | Config]. all() -> @@ -78,6 +80,7 @@ normal_case(Config) -> default_libs, false), {DiscoverProvider, {ok, State1}} = rlx_provider:new(rlx_prv_discover, State0), {ok, State2} = rlx_provider:do(DiscoverProvider, State1), + lists:foreach(fun(App) -> ?assertMatch(true, lists:member(App, rlx_state:available_apps(State2))) end, Apps1), diff --git a/test/rlx_release_SUITE.erl b/test/rlx_release_SUITE.erl index 730e1d1..0382a26 100644 --- a/test/rlx_release_SUITE.erl +++ b/test/rlx_release_SUITE.erl @@ -60,9 +60,11 @@ init_per_testcase(_, Config) -> DataDir = proplists:get_value(data_dir, Config), LibDir1 = filename:join([DataDir, create_random_name("lib_dir1_")]), ok = rlx_util:mkdir_p(LibDir1), - State = rlx_state:new([{lib_dirs, [LibDir1]}], [release]), + 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), [{lib1, LibDir1}, - {state, State} | Config]. + {state, State2} | Config]. all() -> [make_release, make_extend_release, make_scriptless_release, @@ -563,10 +565,10 @@ overlay_release(Config) -> ?assert(lists:member({goal_app_2, "0.0.1"}, AppSpecs)), ?assert(lists:member({lib_dep_1, "0.0.1", load}, AppSpecs)), - ?assert(ec_file:exists(filename:join([OutputDir, "foo", "fooo"]))), - ?assert(ec_file:exists(filename:join([OutputDir, "foo", "foodir", "vars1.config"]))), - ?assert(ec_file:exists(filename:join([OutputDir, "foo", "yahoo", "vars1.config"]))), - ?assert(ec_file:exists(filename:join([OutputDir, "foo", SecondTestDir, TestDir, TestFile]))), + ?assert(ec_file:exists(filename:join(OutputDir, "fooo"))), + ?assert(ec_file:exists(filename:join([OutputDir, "foodir", "vars1.config"]))), + ?assert(ec_file:exists(filename:join([OutputDir, "yahoo", "vars1.config"]))), + ?assert(ec_file:exists(filename:join([OutputDir, SecondTestDir, TestDir, TestFile]))), TemplateData = case file:consult(filename:join([OutputDir, "foo", "test_template_resolved"])) of {ok, Details} -> -- cgit v1.2.3 From dd55959854069553b40f2bc2f2f7c5c7fc7c5a94 Mon Sep 17 00:00:00 2001 From: Tristan Sloughter Date: Fri, 9 May 2014 08:45:24 -0500 Subject: refactor tar and relup commands to own providers --- src/relx.erl | 4 +- src/rlx_prv_archive.erl | 116 ++++++++++++++++++++ src/rlx_prv_assembler.erl | 267 +++------------------------------------------ src/rlx_prv_config.erl | 5 - src/rlx_prv_discover.erl | 2 +- src/rlx_prv_relup.erl | 154 ++++++++++++++++++++++++++ src/rlx_state.erl | 34 ++++-- src/rlx_util.erl | 35 +++++- test/rlx_command_SUITE.erl | 2 +- test/rlx_release_SUITE.erl | 9 +- 10 files changed, 355 insertions(+), 273 deletions(-) create mode 100644 src/rlx_prv_archive.erl create mode 100644 src/rlx_prv_relup.erl diff --git a/src/relx.erl b/src/relx.erl index 79eeae2..769668a 100644 --- a/src/relx.erl +++ b/src/relx.erl @@ -118,7 +118,7 @@ do(RootDir, RelName, RelVsn, Goals, LibDirs, LogLevel, OutputDir, Configs) -> -spec do(file:name(), atom(), string(), [goal()], [file:name()], ec_cmd_log:log_level(), [file:name()], [{atom(), file:name()}], file:name() | undefined) -> ok | error() | {ok, rlx_state:t()}. -do(RootDir, RelName, RelVsn, Goals, LibDirs, LogLevel, OutputDir, Overrides, Config) -> +do(RootDir, RelName, RelVsn, Goals, LibDirs, LogLevel, OutputDir, Overrides, Config) -> do([{relname, RelName}, {relvsn, RelVsn}, {goals, Goals}, @@ -258,7 +258,7 @@ handle_output(_State, command_line, _) -> handle_output(_State, api, Result) -> Result. -run_providers(ConfigProvider, Providers, State0) -> +run_providers(ConfigProvider, Providers, State0) -> case Providers of [ConfigProvider | Rest] -> %% IF the config provider is still the first provider do not run it diff --git a/src/rlx_prv_archive.erl b/src/rlx_prv_archive.erl new file mode 100644 index 0000000..3ad06b4 --- /dev/null +++ b/src/rlx_prv_archive.erl @@ -0,0 +1,116 @@ +%% -*- erlang-indent-level: 4; indent-tabs-mode: nil; fill-column: 80 -*- +%%% Copyright 2014 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 Tristan Sloughter +%%% @copyright (C) 2014 Erlware, LLC. +%%% +%%% @doc Given a complete built release this provider assembles that release +%%% into a release directory. +-module(rlx_prv_archive). + +-behaviour(rlx_provider). + +-export([init/1, + do/1, + format_error/1]). + +-include("relx.hrl"). + +%%============================================================================ +%% API +%%============================================================================ +-spec init(rlx_state:t()) -> {ok, rlx_state:t()}. +init(State) -> + {ok, State}. + +-spec do(rlx_state:t()) -> {ok, rlx_state:t()} | relx:error(). +do(State) -> + {RelName, RelVsn} = rlx_state:default_configured_release(State), + Release = rlx_state:get_realized_release(State, RelName, RelVsn), + OutputDir = rlx_state:output_dir(State), + make_tar(State, Release, OutputDir). + +format_error({tar_unknown_generation_error, Module, Vsn}) -> + io_lib:format("Tarball generation error of ~s ~s", + [Module, Vsn]); +format_error({tar_generation_warn, Module, Warnings}) -> + io_lib:format("Tarball generation warnings for ~p : ~p", + [Module, Warnings]); +format_error({tar_generation_error, Module, Errors}) -> + io_lib:format("Tarball generation error for ~p reason ~p", + [Module, Errors]). + +make_tar(State, Release, OutputDir) -> + Name = atom_to_list(rlx_release:name(Release)), + Vsn = rlx_release:vsn(Release), + ErtsVersion = rlx_release:erts(Release), + Opts = [{path, [filename:join([OutputDir, "lib", "*", "ebin"])]}, + {outdir, OutputDir} | + case rlx_state:get(State, include_erts, true) of + true -> + Prefix = code:root_dir(), + ErtsDir = filename:join([Prefix]), + [{erts, ErtsDir}]; + false -> + []; + Prefix -> + ErtsDir = filename:join([Prefix]), + [{erts, ErtsDir}] + end], + case systools:make_tar(filename:join([OutputDir, "releases", Vsn, Name]), + Opts) of + ok -> + TempDir = ec_file:insecure_mkdtemp(), + try + update_tar(State, TempDir, OutputDir, Name, Vsn, ErtsVersion) + catch + E:R -> + ec_file:remove(TempDir, [recursive]), + ?RLX_ERROR({tar_generation_error, E, R}) + end; + {ok, Module, Warnings} -> + ?RLX_ERROR({tar_generation_warn, Module, Warnings}); + error -> + ?RLX_ERROR({tar_unknown_generation_error, Name, Vsn}); + {error, Module, Errors} -> + ?RLX_ERROR({tar_generation_error, Module, Errors}) + end. + +update_tar(State, TempDir, OutputDir, Name, Vsn, ErtsVersion) -> + TarFile = filename:join(OutputDir, Name++"-"++Vsn++".tar.gz"), + file:rename(filename:join(OutputDir, Name++".tar.gz"), TarFile), + erl_tar:extract(TarFile, [{cwd, TempDir}, compressed]), + ok = + erl_tar:create(TarFile, + [{"lib", filename:join(TempDir, "lib")}, + {"releases", filename:join(TempDir, "releases")}, + {filename:join(["releases", "RELEASES"]), + filename:join([OutputDir, "releases", "RELEASES"])}, + {filename:join(["releases", Vsn, "vm.args"]), + filename:join([OutputDir, "releases", Vsn, "vm.args"])}, + {"bin", filename:join([OutputDir, "bin"])} | + case rlx_state:get(State, include_erts, true) of + false -> + []; + _ -> + [{"erts-"++ErtsVersion, filename:join(OutputDir, "erts-"++ErtsVersion)}] + end], [compressed]), + ec_cmd_log:info(rlx_state:log(State), + "tarball ~s successfully created!~n", [TarFile]), + ec_file:remove(TempDir, [recursive]), + {ok, State}. + diff --git a/src/rlx_prv_assembler.erl b/src/rlx_prv_assembler.erl index 1afd18d..827b4c1 100644 --- a/src/rlx_prv_assembler.erl +++ b/src/rlx_prv_assembler.erl @@ -40,49 +40,21 @@ init(State) -> %% @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 -> - run_actions(State, Release, OutputDir); + true -> + copy_app_directories_to_output(State, Release, OutputDir); false -> ?RLX_ERROR({unresolved_release, RelName, RelVsn}) end; Error -> - Error - end. - -do(release, State, Release, OutputDir) -> - copy_app_directories_to_output(State, Release, OutputDir); -do(relup, State, Release, _OutputDir) -> - RelName = rlx_release:name(Release), - RelVsn = rlx_release:vsn(Release), - Release0 = rlx_state:get_realized_release(State, RelName, RelVsn), - make_relup(State, Release0); -do(tar, State, Release, OutputDir) -> - make_tar(State, Release, OutputDir). - -run_actions(State, Release, OutputDir) -> - run_actions(State, Release, OutputDir, rlx_state:actions(State), [release, relup, tar]). - -run_actions(State, _Release, _OutputDir, _Actions, []) -> - {ok, State}; -run_actions(State, Release, OutputDir, Actions, [H | T]) -> - case lists:member(H, Actions) of - true -> - case do(H, State, Release, OutputDir) of - {ok, NewState} -> - run_actions(NewState, Release, OutputDir, Actions, T); - Error -> - Error - end; - false -> - run_actions(State, Release, OutputDir, Actions, T) + Error end. -spec format_error(ErrorDetail::term()) -> iolist(). @@ -109,37 +81,10 @@ format_error({unable_to_create_output_dir, OutputDir}) -> format_error({release_script_generation_error, Module, Errors}) -> ["Errors generating release \n", rlx_util:indent(2), Module:format_error(Errors)]; -format_error({relup_generation_error, CurrentName, UpFromName}) -> - io_lib:format("Unknown internal release error generating the relup from ~s to ~s", - [UpFromName, CurrentName]); -format_error({relup_generation_warning, Module, Warnings}) -> - ["Warnings generating relup \s", - rlx_util:indent(2), Module:format_warning(Warnings)]; -format_error({no_upfrom_release_found, undefined}) -> - io_lib:format("No earlier release for relup found", []); -format_error({no_upfrom_release_found, Vsn}) -> - io_lib:format("Upfrom release version (~s) for relup not found", [Vsn]); -format_error({relup_script_generation_error, - {relup_script_generation_error, systools_relup, - {missing_sasl, _}}}) -> - "Unfortunately, due to requirements in systools, you need to have the sasl application " - "in both the current release and the release to upgrade from."; -format_error({relup_script_generation_error, Module, Errors}) -> - ["Errors generating relup \n", - rlx_util:indent(2), Module:format_error(Errors)]; format_error({unable_to_make_symlink, AppDir, TargetDir, Reason}) -> io_lib:format("Unable to symlink directory ~s to ~s because \n~s~s", [AppDir, TargetDir, rlx_util:indent(2), - file:format_error(Reason)]); -format_error({tar_unknown_generation_error, Module, Vsn}) -> - io_lib:format("Tarball generation error of ~s ~s", - [Module, Vsn]); -format_error({tar_generation_warn, Module, Warnings}) -> - io_lib:format("Tarball generation warnings for ~p : ~p", - [Module, Warnings]); -format_error({tar_generation_error, Module, Errors}) -> - io_lib:format("Tarball generation error for ~p reason ~p", - [Module, Errors]). + file:format_error(Reason)]). %%%=================================================================== %%% Internal Functions @@ -174,13 +119,13 @@ copy_app_directories_to_output(State, Release, OutputDir) -> IncludeSrc = rlx_state:include_src(State), Apps = prepare_applications(State, rlx_release:application_details(Release)), Result = lists:filter(fun({error, _}) -> - true; - (_) -> - false - end, - lists:flatten(ec_plists:map(fun(App) -> - copy_app(LibDir, App, IncludeSrc) - end, Apps))), + true; + (_) -> + false + end, + lists:flatten(ec_plists:map(fun(App) -> + copy_app(LibDir, App, IncludeSrc) + end, Apps))), case Result of [E | _] -> E; @@ -274,7 +219,7 @@ copy_dir(AppDir, TargetDir, SubDir) -> create_release_info(State0, Release0, OutputDir) -> RelName = atom_to_list(rlx_release:name(Release0)), - ReleaseDir = release_output_dir(State0, Release0), + ReleaseDir = rlx_util:release_output_dir(State0, Release0), ReleaseFile = filename:join([ReleaseDir, RelName ++ ".rel"]), ok = ec_file:mkdir_p(ReleaseDir), Release1 = rlx_release:relfile(Release0, ReleaseFile), @@ -287,7 +232,6 @@ create_release_info(State0, Release0, OutputDir) -> E end. - write_bin_file(State, Release, OutputDir, RelDir) -> RelName = erlang:atom_to_list(rlx_release:name(Release)), RelVsn = rlx_release:vsn(Release), @@ -466,12 +410,12 @@ include_erts(State, Release, OutputDir, RelDir) -> -spec make_boot_script(rlx_state:t(), rlx_release:t(), file:name(), file:name()) -> {ok, rlx_state:t()} | relx:error(). make_boot_script(State, Release, OutputDir, RelDir) -> - Options = [{path, [RelDir | get_code_paths(Release, OutputDir)]}, + Options = [{path, [RelDir | rlx_util:get_code_paths(Release, OutputDir)]}, {outdir, RelDir}, no_module_tests, silent], Name = erlang:atom_to_list(rlx_release:name(Release)), ReleaseFile = filename:join([RelDir, Name ++ ".rel"]), - case make_script(Options, + case rlx_util:make_script(Options, fun(CorrectedOptions) -> systools:make_script(Name, CorrectedOptions) end) of @@ -493,95 +437,6 @@ make_boot_script(State, Release, OutputDir, RelDir) -> ?RLX_ERROR({release_script_generation_error, Module, Error}) end. --spec make_script([term()], - fun(([term()]) -> Res)) -> Res. -make_script(Options, RunFun) -> - %% Erts 5.9 introduced a non backwards compatible option to - %% erlang this takes that into account - Erts = erlang:system_info(version), - case ec_semver:gte(Erts, "5.9") of - true -> - RunFun([no_warn_sasl | Options]); - _ -> - RunFun(Options) - end. - -make_relup(State, Release) -> - Vsn = rlx_state:upfrom(State), - UpFrom = - case Vsn of - undefined -> - get_last_release(State, Release); - Vsn -> - get_up_release(State, Release, Vsn) - end, - case UpFrom of - undefined -> - ?RLX_ERROR({no_upfrom_release_found, Vsn}); - _ -> - make_upfrom_script(State, Release, UpFrom) - end. - -make_tar(State, Release, OutputDir) -> - Name = atom_to_list(rlx_release:name(Release)), - Vsn = rlx_release:vsn(Release), - ErtsVersion = rlx_release:erts(Release), - Opts = [{path, [filename:join([OutputDir, "lib", "*", "ebin"])]}, - {outdir, OutputDir} | - case rlx_state:get(State, include_erts, true) of - true -> - Prefix = code:root_dir(), - ErtsDir = filename:join([Prefix]), - [{erts, ErtsDir}]; - false -> - []; - Prefix -> - ErtsDir = filename:join([Prefix]), - [{erts, ErtsDir}] - end], - case systools:make_tar(filename:join([OutputDir, "releases", Vsn, Name]), - Opts) of - ok -> - TempDir = ec_file:insecure_mkdtemp(), - try - update_tar(State, TempDir, OutputDir, Name, Vsn, ErtsVersion) - catch - E:R -> - ec_file:remove(TempDir, [recursive]), - ?RLX_ERROR({tar_generation_error, E, R}) - end; - {ok, Module, Warnings} -> - ?RLX_ERROR({tar_generation_warn, Module, Warnings}); - error -> - ?RLX_ERROR({tar_unknown_generation_error, Name, Vsn}); - {error, Module, Errors} -> - ?RLX_ERROR({tar_generation_error, Module, Errors}) - end. - -update_tar(State, TempDir, OutputDir, Name, Vsn, ErtsVersion) -> - TarFile = filename:join(OutputDir, Name++"-"++Vsn++".tar.gz"), - file:rename(filename:join(OutputDir, Name++".tar.gz"), TarFile), - erl_tar:extract(TarFile, [{cwd, TempDir}, compressed]), - ok = - erl_tar:create(TarFile, - [{"lib", filename:join(TempDir, "lib")}, - {"releases", filename:join(TempDir, "releases")}, - {filename:join(["releases", "RELEASES"]), - filename:join([OutputDir, "releases", "RELEASES"])}, - {filename:join(["releases", Vsn, "vm.args"]), - filename:join([OutputDir, "releases", Vsn, "vm.args"])}, - {"bin", filename:join([OutputDir, "bin"])} | - case rlx_state:get(State, include_erts, true) of - false -> - []; - _ -> - [{"erts-"++ErtsVersion, filename:join(OutputDir, "erts-"++ErtsVersion)}] - end], [compressed]), - ec_cmd_log:info(rlx_state:log(State), - "tarball ~s successfully created!~n", [TarFile]), - ec_file:remove(TempDir, [recursive]), - {ok, State}. - create_RELEASES(OutputDir, ReleaseFile) -> {ok, OldCWD} = file:get_cwd(), file:set_cwd(OutputDir), @@ -591,96 +446,6 @@ create_RELEASES(OutputDir, ReleaseFile) -> []), file:set_cwd(OldCWD). -make_upfrom_script(State, Release, UpFrom) -> - OutputDir = rlx_state:output_dir(State), - Options = [{outdir, OutputDir}, - {path, get_code_paths(Release, OutputDir) ++ - get_code_paths(UpFrom, OutputDir)}, - silent], - CurrentRel = strip_rel(rlx_release:relfile(Release)), - UpFromRel = strip_rel(rlx_release:relfile(UpFrom)), - ec_cmd_log:debug(rlx_state:log(State), - "systools:make_relup(~p, ~p, ~p, ~p)", - [CurrentRel, UpFromRel, UpFromRel, Options]), - case make_script(Options, - fun(CorrectOptions) -> - systools:make_relup(CurrentRel, [UpFromRel], [UpFromRel], CorrectOptions) - end) of - ok -> - ec_cmd_log:error(rlx_state:log(State), - "relup from ~s to ~s successfully created!", - [UpFromRel, CurrentRel]), - {ok, State}; - error -> - ?RLX_ERROR({relup_script_generation_error, CurrentRel, UpFromRel}); - {ok, RelUp, _, []} -> - ec_cmd_log:error(rlx_state:log(State), - "relup successfully created!"), - write_relup_file(State, Release, RelUp), - {ok, State}; - {ok,_, Module,Warnings} -> - ?RLX_ERROR({relup_script_generation_warn, Module, Warnings}); - {error,Module,Errors} -> - ?RLX_ERROR({relup_script_generation_error, Module, Errors}) - end. - -write_relup_file(State, Release, Relup) -> - OutDir = release_output_dir(State, Release), - RelupFile = filename:join(OutDir, "relup"), - ok = ec_file:write_term(RelupFile, Relup). - -strip_rel(Name) -> - rlx_util:to_string(filename:join(filename:dirname(Name), - filename:basename(Name, ".rel"))). - -get_up_release(State, Release, Vsn) -> - Name = rlx_release:name(Release), - try - ec_dictionary:get({Name, Vsn}, rlx_state:realized_releases(State)) - catch - throw:not_found -> - undefined - end. - -get_last_release(State, Release) -> - Releases0 = [Rel || {{_, _}, Rel} <- ec_dictionary:to_list(rlx_state:realized_releases(State))], - Releases1 = lists:sort(fun(R1, R2) -> - ec_semver:lte(rlx_release:vsn(R1), - rlx_release:vsn(R2)) - end, Releases0), - Res = lists:foldl(fun(_Rel, R = {found, _}) -> - R; - (Rel, Prev) -> - case rlx_release:vsn(Rel) == rlx_release:vsn(Release) of - true -> - {found, Prev}; - false -> - Rel - end - end, undefined, Releases1), - case Res of - {found, R} -> - R; - Else -> - Else - end. - --spec release_output_dir(rlx_state:t(), rlx_release:t()) -> string(). -release_output_dir(State, Release) -> - OutputDir = rlx_state:output_dir(State), - filename:join([OutputDir, - "releases", - rlx_release:vsn(Release)]). - -%% @doc Generates the correct set of code paths for the system. --spec get_code_paths(rlx_release:t(), file:name()) -> [file:name()]. -get_code_paths(Release, OutDir) -> - LibDir = filename:join(OutDir, "lib"), - [filename:join([LibDir, - erlang:atom_to_list(rlx_app_info:name(App)) ++ "-" ++ - rlx_app_info:original_vsn(App), "ebin"]) || - App <- rlx_release:application_details(Release)]. - unless_exists_write_default(Path, File) -> case ec_file:exists(Path) of true -> diff --git a/src/rlx_prv_config.erl b/src/rlx_prv_config.erl index dd7dc70..c795334 100644 --- a/src/rlx_prv_config.erl +++ b/src/rlx_prv_config.erl @@ -137,11 +137,6 @@ load_terms({system_libs, SystemLibs}, {ok, State}) -> system_libs, SystemLibs), {ok, State2}; -load_terms({overlay_vars, OverlayVars}, {ok, State}) -> - State2 = rlx_state:put(State, - overlay_vars, - list_of_overlay_vars_files(OverlayVars)), - {ok, State2}; load_terms({lib_dirs, Dirs}, {ok, State}) -> State2 = rlx_state:add_lib_dirs(State, diff --git a/src/rlx_prv_discover.erl b/src/rlx_prv_discover.erl index 5d09619..21e5687 100644 --- a/src/rlx_prv_discover.erl +++ b/src/rlx_prv_discover.erl @@ -128,7 +128,7 @@ add_release_output_dir(State) -> true -> []; false -> - Output = erlang:iolist_to_binary(rlx_state:output_dir(State)), + Output = erlang:iolist_to_binary(rlx_state:base_output_dir(State)), case ec_file:exists(erlang:binary_to_list(Output)) of true -> Output; diff --git a/src/rlx_prv_relup.erl b/src/rlx_prv_relup.erl new file mode 100644 index 0000000..241d45b --- /dev/null +++ b/src/rlx_prv_relup.erl @@ -0,0 +1,154 @@ +%% -*- erlang-indent-level: 4; indent-tabs-mode: nil; fill-column: 80 -*- +%%% Copyright 2014 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 Tristan Sloughter +%%% @copyright (C) 2014 Erlware, LLC. +%%% +%%% @doc Given a complete built release this provider assembles that release +%%% into a release directory. +-module(rlx_prv_relup). + +-behaviour(rlx_provider). + +-export([init/1, + do/1, + format_error/1]). + +-include("relx.hrl"). + +%%============================================================================ +%% API +%%============================================================================ +-spec init(rlx_state:t()) -> {ok, rlx_state:t()}. +init(State) -> + {ok, State}. + +-spec do(rlx_state:t()) -> {ok, rlx_state:t()} | relx:error(). +do(State) -> + {RelName, RelVsn} = rlx_state:default_configured_release(State), + Release0 = rlx_state:get_realized_release(State, RelName, RelVsn), + make_relup(State, Release0). + +format_error({relup_generation_error, CurrentName, UpFromName}) -> + io_lib:format("Unknown internal release error generating the relup from ~s to ~s", + [UpFromName, CurrentName]); +format_error({relup_generation_warning, Module, Warnings}) -> + ["Warnings generating relup \s", + rlx_util:indent(2), Module:format_warning(Warnings)]; +format_error({no_upfrom_release_found, undefined}) -> + io_lib:format("No earlier release for relup found", []); +format_error({no_upfrom_release_found, Vsn}) -> + io_lib:format("Upfrom release version (~s) for relup not found", [Vsn]); +format_error({relup_script_generation_error, + {relup_script_generation_error, systools_relup, + {missing_sasl, _}}}) -> + "Unfortunately, due to requirements in systools, you need to have the sasl application " + "in both the current release and the release to upgrade from."; +format_error({relup_script_generation_error, Module, Errors}) -> + ["Errors generating relup \n", + rlx_util:indent(2), Module:format_error(Errors)]. + +make_relup(State, Release) -> + Vsn = rlx_state:upfrom(State), + UpFrom = + case Vsn of + undefined -> + get_last_release(State, Release); + Vsn -> + get_up_release(State, Release, Vsn) + end, + case UpFrom of + undefined -> + ?RLX_ERROR({no_upfrom_release_found, Vsn}); + _ -> + make_upfrom_script(State, Release, UpFrom) + end. + +get_last_release(State, Release) -> + Releases0 = [Rel || {{_, _}, Rel} <- ec_dictionary:to_list(rlx_state:realized_releases(State))], + Releases1 = lists:sort(fun(R1, R2) -> + ec_semver:lte(rlx_release:vsn(R1), + rlx_release:vsn(R2)) + end, Releases0), + Res = lists:foldl(fun(_Rel, R = {found, _}) -> + R; + (Rel, Prev) -> + case rlx_release:vsn(Rel) == rlx_release:vsn(Release) of + true -> + {found, Prev}; + false -> + Rel + end + end, undefined, Releases1), + case Res of + {found, R} -> + R; + Else -> + Else + end. + +get_up_release(State, Release, Vsn) -> + Name = rlx_release:name(Release), + try + ec_dictionary:get({Name, Vsn}, rlx_state:realized_releases(State)) + catch + throw:not_found -> + undefined + end. + +make_upfrom_script(State, Release, UpFrom) -> + OutputDir = rlx_state:output_dir(State), + Options = [{outdir, OutputDir}, + {path, rlx_util:get_code_paths(Release, OutputDir) ++ + rlx_util:get_code_paths(UpFrom, OutputDir)}, + silent], + CurrentRel = strip_rel(rlx_release:relfile(Release)), + UpFromRel = strip_rel(rlx_release:relfile(UpFrom)), + ec_cmd_log:debug(rlx_state:log(State), + "systools:make_relup(~p, ~p, ~p, ~p)", + [CurrentRel, UpFromRel, UpFromRel, Options]), + case rlx_util:make_script(Options, + fun(CorrectOptions) -> + systools:make_relup(CurrentRel, [UpFromRel], [UpFromRel], CorrectOptions) + end) of + ok -> + ec_cmd_log:error(rlx_state:log(State), + "relup from ~s to ~s successfully created!", + [UpFromRel, CurrentRel]), + {ok, State}; + error -> + ?RLX_ERROR({relup_script_generation_error, CurrentRel, UpFromRel}); + {ok, RelUp, _, []} -> + ec_cmd_log:error(rlx_state:log(State), + "relup successfully created!"), + write_relup_file(State, Release, RelUp), + {ok, State}; + {ok,_, Module,Warnings} -> + ?RLX_ERROR({relup_script_generation_warn, Module, Warnings}); + {error,Module,Errors} -> + ?RLX_ERROR({relup_script_generation_error, Module, Errors}) + end. + +write_relup_file(State, Release, Relup) -> + OutDir = rlx_util:release_output_dir(State, Release), + RelupFile = filename:join(OutDir, "relup"), + ok = ec_file:write_term(RelupFile, Relup). + +strip_rel(Name) -> + rlx_util:to_string(filename:join(filename:dirname(Name), + filename:basename(Name, ".rel"))). + diff --git a/src/rlx_state.erl b/src/rlx_state.erl index c84be78..d212dcf 100644 --- a/src/rlx_state.erl +++ b/src/rlx_state.erl @@ -135,8 +135,7 @@ new(Config, CommandLineConfig, Targets) {ok, Root} = file:get_cwd(), Caller = proplists:get_value(caller, CommandLineConfig, api), - Log = proplists:get_value(log, CommandLineConfig, ec_cmd_log:new(error, Caller)), - + Log = proplists:get_value(log, CommandLineConfig, ec_cmd_log:new(error, Caller)), State0 = #state_t{log=Log, config_file=Config, cli_args=CommandLineConfig, @@ -148,7 +147,7 @@ new(Config, CommandLineConfig, Targets) default_configured_release=undefined, configured_releases=ec_dictionary:new(ec_dict), realized_releases=ec_dictionary:new(ec_dict), - config_values=ec_dictionary:new(ec_dict)}, + config_values=ec_dictionary:new(ec_dict)}, State1 = rlx_state:put(State0, default_libs, true), State2 = rlx_state:put(State1, system_libs, undefined), State3 = rlx_state:put(State2, overlay_vars, []), @@ -404,10 +403,31 @@ 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), - {AssemblerProvider, {ok, State5}} = rlx_provider:new(rlx_prv_assembler, State4), + {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, AssemblerProvider]}. + 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) + 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 @@ -418,7 +438,7 @@ create_logic_providers(State0) -> new_test() -> LogState = ec_cmd_log:new(error), - RCLState = new(LogState, [], [release]), + RCLState = new("", [{log, LogState}], [release]), ?assertMatch(LogState, log(RCLState)). -endif. diff --git a/src/rlx_util.erl b/src/rlx_util.erl index 48e2ee1..9c4dcc2 100644 --- a/src/rlx_util.erl +++ b/src/rlx_util.erl @@ -21,7 +21,10 @@ %%% @doc Trivial utility file to help handle common tasks -module(rlx_util). --export([mkdir_p/1, +-export([get_code_paths/2, + release_output_dir/2, + make_script/2, + mkdir_p/1, to_binary/1, to_string/1, to_atom/1, @@ -40,6 +43,36 @@ %%============================================================================ %% API %%============================================================================ + +%% @doc Generates the correct set of code paths for the system. +-spec get_code_paths(rlx_release:t(), file:name()) -> [file:name()]. +get_code_paths(Release, OutDir) -> + LibDir = filename:join(OutDir, "lib"), + [filename:join([LibDir, + erlang:atom_to_list(rlx_app_info:name(App)) ++ "-" ++ + rlx_app_info:original_vsn(App), "ebin"]) || + App <- rlx_release:application_details(Release)]. + +-spec release_output_dir(rlx_state:t(), rlx_release:t()) -> string(). +release_output_dir(State, Release) -> + OutputDir = rlx_state:output_dir(State), + filename:join([OutputDir, + "releases", + rlx_release:vsn(Release)]). + +-spec make_script([term()], + fun(([term()]) -> Res)) -> Res. +make_script(Options, RunFun) -> + %% Erts 5.9 introduced a non backwards compatible option to + %% erlang this takes that into account + Erts = erlang:system_info(version), + case ec_semver:gte(Erts, "5.9") of + true -> + RunFun([no_warn_sasl | Options]); + _ -> + RunFun(Options) + end. + %% @doc Makes a directory including parent dirs if they are missing. -spec mkdir_p(string()) -> ok | {error, Reason::file:posix()}. mkdir_p(Path) -> diff --git a/test/rlx_command_SUITE.erl b/test/rlx_command_SUITE.erl index e26e0ad..c34ed88 100644 --- a/test/rlx_command_SUITE.erl +++ b/test/rlx_command_SUITE.erl @@ -65,7 +65,7 @@ normal_passing_case(Config) -> {ok, State2} = rlx_provider:do(ConfigProvider, State1), ?assertMatch([Lib1, Lib2], rlx_state:lib_dirs(State2)), - ?assertMatch(Outdir, rlx_state:output_dir(State2)), + ?assertMatch(Outdir, rlx_state:base_output_dir(State2)), ?assertMatch([{app1,{{33,33},{[],[<<"build4">>]}},lte}, {app2, diff --git a/test/rlx_release_SUITE.erl b/test/rlx_release_SUITE.erl index 0382a26..6f067dc 100644 --- a/test/rlx_release_SUITE.erl +++ b/test/rlx_release_SUITE.erl @@ -222,7 +222,6 @@ make_scriptless_release(Config) -> ?assert(lists:member({goal_app_2, "0.0.1"}, AppSpecs)), ?assert(lists:member({lib_dep_1, "0.0.1", load}, AppSpecs)). - make_overridden_release(Config) -> DataDir = proplists:get_value(data_dir, Config), OverrideDir1 = filename:join([DataDir, create_random_name("override_dir_")]), @@ -565,10 +564,10 @@ overlay_release(Config) -> ?assert(lists:member({goal_app_2, "0.0.1"}, AppSpecs)), ?assert(lists:member({lib_dep_1, "0.0.1", load}, AppSpecs)), - ?assert(ec_file:exists(filename:join(OutputDir, "fooo"))), - ?assert(ec_file:exists(filename:join([OutputDir, "foodir", "vars1.config"]))), - ?assert(ec_file:exists(filename:join([OutputDir, "yahoo", "vars1.config"]))), - ?assert(ec_file:exists(filename:join([OutputDir, SecondTestDir, TestDir, TestFile]))), + ?assert(ec_file:exists(filename:join([OutputDir, "foo", "fooo"]))), + ?assert(ec_file:exists(filename:join([OutputDir, "foo", "foodir", "vars1.config"]))), + ?assert(ec_file:exists(filename:join([OutputDir, "foo", "yahoo", "vars1.config"]))), + ?assert(ec_file:exists(filename:join([OutputDir, "foo", SecondTestDir, TestDir, TestFile]))), TemplateData = case file:consult(filename:join([OutputDir, "foo", "test_template_resolved"])) of {ok, Details} -> -- cgit v1.2.3