diff options
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | include/relx.hrl | 5 | ||||
-rw-r--r-- | src/relx.erl | 4 | ||||
-rw-r--r-- | src/rlx_app_discovery.erl | 19 | ||||
-rw-r--r-- | src/rlx_cmd_args.erl | 4 | ||||
-rw-r--r-- | src/rlx_dscv_util.erl | 2 | ||||
-rw-r--r-- | src/rlx_log.erl | 33 | ||||
-rw-r--r-- | src/rlx_prv_assembler.erl | 44 | ||||
-rw-r--r-- | src/rlx_rel_discovery.erl | 2 | ||||
-rw-r--r-- | test/rlx_discover_SUITE.erl | 9 | ||||
-rw-r--r-- | test/rlx_release_SUITE.erl | 42 |
11 files changed, 102 insertions, 64 deletions
@@ -57,7 +57,7 @@ Options | -l | --lib-dir | string | | Additional dirs to search for OTP apps | | -p | --path | string | | Additional dirs to add to Erlang code path | | | --default-libs | boolean | true | Whether to use the default system added lib dirs (means you must add them all manually) | -| -V | --verbose | integer | 0 | The verbosity level between 0 and 2 | +| -V | --verbose | integer | 2 | The verbosity level between 0 and 3 | | -a | --override_app | string | | An app name and a directory to override in the form appname:dir | | -c | --config | string | ./relx.config | Config file path | diff --git a/include/relx.hrl b/include/relx.hrl index 4b78ca6..ee575a2 100644 --- a/include/relx.hrl +++ b/include/relx.hrl @@ -17,8 +17,9 @@ %% -define(RLX_ERROR, 0). --define(RLX_INFO, 1). --define(RLX_DEBUG, 2). +-define(RLX_WARN, 1). +-define(RLX_INFO, 2). +-define(RLX_DEBUG, 3). %% This is the default form of error messages for the Relx %% system. It is expected that everything that returns an error use diff --git a/src/relx.erl b/src/relx.erl index f6575a1..f6e156d 100644 --- a/src/relx.erl +++ b/src/relx.erl @@ -195,8 +195,8 @@ opt_spec_list() -> {default_libs, undefined, "default-libs", {boolean, true}, "Whether to use the default system added lib dirs (means you must add them all manually). Default is true"}, - {log_level, $V, "verbose", {integer, 1}, - "Verbosity level, maybe between 0 and 2"}, + {log_level, $V, "verbose", {integer, 2}, + "Verbosity level, maybe between 0 and 3"}, {override_app, $a, "override_app", string, "Provide an app name and a directory to override in the form <appname>:<app directory>"}, {config, $c, "config", {string, ""}, "The path to a config file"}, diff --git a/src/rlx_app_discovery.erl b/src/rlx_app_discovery.erl index 39f3921..cf0d1c6 100644 --- a/src/rlx_app_discovery.erl +++ b/src/rlx_app_discovery.erl @@ -39,8 +39,8 @@ do(State, LibDirs) -> rlx_log:info(rlx_state:log(State), fun() -> - ["Resolving OTP Applications from directories:\n", - [[rlx_util:indent(2), LibDir, "\n"] || LibDir <- LibDirs]] + ["Resolving OTP Applications from directories:\n", + string:join([[rlx_util:indent(2), LibDir] || LibDir <- LibDirs], "\n")] end), resolve_app_metadata(State, LibDirs). @@ -62,6 +62,9 @@ resolve_app_metadata(State, LibDirs) -> case Err of {error, _} -> true; + {warning, W} -> + rlx_log:warn(rlx_state:log(State), format_detail(W)), + false; _ -> false end] of @@ -79,6 +82,8 @@ resolve_app_metadata(State, LibDirs) -> ?RLX_ERROR(Errors) end. +app_name({warning, _}) -> + undefined; app_name({error, _}) -> undefined; app_name({ok, AppMeta}) -> @@ -103,7 +108,7 @@ resolve_override(AppName, FileName0) -> end. -spec format_detail(ErrorDetail::term()) -> iolist(). -format_detail({error, {missing_beam_file, Module, BeamFile}}) -> +format_detail({missing_beam_file, Module, BeamFile}) -> io_lib:format("Missing beam file ~p ~p", [Module, BeamFile]); format_detail({error, {invalid_override, AppName, FileName}}) -> io_lib:format("Override {~p, ~p} is not a valid OTP App. Perhaps you forgot to build it?", @@ -161,9 +166,9 @@ gather_application_info(EbinDir, File) -> {ok, [{application, AppName, AppDetail}]} -> validate_application_info(EbinDir, File, AppName, AppDetail); {error, Reason} -> - {error, {unable_to_load_app, AppDir, Reason}}; + {warning, {unable_to_load_app, AppDir, Reason}}; _ -> - {error, {invalid_app_file, File}} + {warning, {invalid_app_file, File}} end. -spec validate_application_info(file:name(), @@ -189,7 +194,7 @@ validate_application_info(EbinDir, AppFile, AppName, AppDetail) -> get_modules_list(AppFile, AppDetail) -> case proplists:get_value(modules, AppDetail) of undefined -> - {error, {invalid_app_file, AppFile}}; + {warning, {invalid_app_file, AppFile}}; ModulesList -> {ok, ModulesList} end. @@ -203,7 +208,7 @@ has_all_beams(EbinDir, [Module | ModuleList]) -> true -> has_all_beams(EbinDir, ModuleList); false -> - {error, {missing_beam_file, Module, BeamFile}} + {warning, {missing_beam_file, Module, BeamFile}} end; has_all_beams(_, []) -> ok. diff --git a/src/rlx_cmd_args.erl b/src/rlx_cmd_args.erl index 621b626..57c9a57 100644 --- a/src/rlx_cmd_args.erl +++ b/src/rlx_cmd_args.erl @@ -80,7 +80,7 @@ format_error({not_directory, Dir}) -> io_lib:format("Library directory does not exist: ~s", [Dir]); format_error({invalid_log_level, LogLevel}) -> io_lib:format("Invalid log level specified -V ~p, log level must be in the" - " range 0..2", [LogLevel]); + " range 0..3", [LogLevel]); format_error({invalid_target, Target}) -> io_lib:format("Invalid action specified: ~s", [Target]). @@ -136,7 +136,7 @@ validate_config(Config) -> create_log(Opts, Acc) -> LogLevel = proplists:get_value(log_level, Opts, 0), if - LogLevel >= 0, LogLevel =< 2 -> + LogLevel >= 0, LogLevel =< 3 -> create_goals(Opts, [{log, rlx_log:new(LogLevel, command_line)} | Acc]); true -> ?RLX_ERROR({invalid_log_level, LogLevel}) diff --git a/src/rlx_dscv_util.erl b/src/rlx_dscv_util.erl index 779534c..52bbd07 100644 --- a/src/rlx_dscv_util.erl +++ b/src/rlx_dscv_util.erl @@ -97,6 +97,8 @@ discover_dir(ProcessDir, File, file) -> [{ok, Result}]; {noresult, _} -> []; + Warn = {warning, _} -> + [Warn]; Err = {error, _} -> [Err] end; diff --git a/src/rlx_log.erl b/src/rlx_log.erl index 608c0af..64997a2 100644 --- a/src/rlx_log.erl +++ b/src/rlx_log.erl @@ -32,6 +32,8 @@ info/3, error/2, error/3, + warn/2, + warn/3, log_level/1, atom_log_level/1, format/1]). @@ -63,11 +65,9 @@ -type log_level() :: int_log_level() | atom_log_level(). --type int_log_level() :: 0..2. +-type int_log_level() :: 0..3. -%% Why no warn? because for our purposes there is no difference between error -%% and warn --type atom_log_level() :: error | info | debug. +-type atom_log_level() :: error | warn | info | debug. -type log_fun() :: fun(() -> iolist()). @@ -83,16 +83,18 @@ new(LogLevel) -> new(LogLevel, api). -new(LogLevel, Caller) when LogLevel >= 0, LogLevel =< 2 -> +new(LogLevel, Caller) when LogLevel >= 0, LogLevel =< 3 -> #state_t{mod=?MODULE, log_level=LogLevel, caller=Caller}; new(AtomLogLevel, Caller) when AtomLogLevel =:= error; + AtomLogLevel =:= warn; AtomLogLevel =:= info; AtomLogLevel =:= debug -> LogLevel = case AtomLogLevel of error -> 0; - info -> 1; - debug -> 2 + warn -> 1; + info -> 2; + debug -> 3 end, new(LogLevel, Caller). @@ -142,6 +144,21 @@ error(LogState, String) -> error(LogState, FormatString, Args) -> log(LogState, ?RLX_ERROR, colorize(LogState, ?GREEN, false, FormatString), Args). +%% @doc log at the warn level given the current log state with a string or +%% format string that returns a function +-spec warn(t(), string() | log_fun()) -> ok. +warn(LogState, Fun) + when erlang:is_function(Fun) -> + log(LogState, ?RLX_WARN, fun() -> colorize(LogState, ?MAGENTA, false, Fun()) end); +warn(LogState, String) -> + warn(LogState, "~s~n", [String]). + +%% @doc log at the warn level given the current log state with a format string +%% and argements @see io:format/2 +-spec warn(t(), string(), [any()]) -> ok. +warn(LogState, FormatString, Args) -> + log(LogState, ?RLX_WARN, colorize(LogState, ?MAGENTA, false, FormatString), Args). + %% @doc Execute the fun passed in if log level is as expected. -spec log(t(), int_log_level(), log_fun()) -> ok. log(#state_t{mod=?MODULE, log_level=DetailLogLevel}, LogLevel, Fun) @@ -178,6 +195,8 @@ log_level(#state_t{mod=?MODULE, log_level=DetailLogLevel}) -> -spec atom_log_level(t()) -> atom_log_level(). atom_log_level(#state_t{mod=?MODULE, log_level=?RLX_ERROR}) -> error; +atom_log_level(#state_t{mod=?MODULE, log_level=?RLX_WARN}) -> + warn; atom_log_level(#state_t{mod=?MODULE, log_level=?RLX_INFO}) -> info; atom_log_level(#state_t{mod=?MODULE, log_level=?RLX_DEBUG}) -> diff --git a/src/rlx_prv_assembler.erl b/src/rlx_prv_assembler.erl index 27dd3c1..9369f5d 100644 --- a/src/rlx_prv_assembler.erl +++ b/src/rlx_prv_assembler.erl @@ -431,10 +431,16 @@ make_tar(State, Release, OutputDir) -> Prefix = code:root_dir(), ErtsVersion = rlx_release:erts(Release), ErtsDir = filename:join([Prefix]), + Opts = [{path, [filename:join([OutputDir, "lib", "*", "ebin"])]}, + {outdir, OutputDir} | + case rlx_state:get(State, include_erts, true) of + true -> + [{erts, ErtsDir}]; + false -> + [] + end], case systools:make_tar(filename:join([OutputDir, "releases", Vsn, Name]), - [{path, [filename:join([OutputDir, "lib", "*", "ebin"])]}, - {erts, ErtsDir}, - {outdir, OutputDir}]) of + Opts) of ok -> TempDir = ec_file:insecure_mkdtemp(), try @@ -456,19 +462,25 @@ 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, - [{"erts-"++ErtsVersion, filename:join(TempDir, "erts-"++ErtsVersion)}, - {filename:join(["erts-"++ErtsVersion, "bin", "nodetool"]), - hd(nodetool_contents())}, - {filename:join(["erts-"++ErtsVersion, "bin", "install_upgrade.escript"]), - hd(install_upgrade_escript_contents())}, - {"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"])}], [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 + true -> + [{"erts-"++ErtsVersion, filename:join(TempDir, "erts-"++ErtsVersion)}, + {filename:join(["erts-"++ErtsVersion, "bin", "nodetool"]), + hd(nodetool_contents())}, + {filename:join(["erts-"++ErtsVersion, "bin", "install_upgrade.escript"]), + hd(install_upgrade_escript_contents())}]; + false -> + [] + end], [compressed]), rlx_log:info(rlx_state:log(State), "tarball ~s successfully created!~n", [TarFile]), ec_file:remove(TempDir, [recursive]), diff --git a/src/rlx_rel_discovery.erl b/src/rlx_rel_discovery.erl index 1616f52..ee9d941 100644 --- a/src/rlx_rel_discovery.erl +++ b/src/rlx_rel_discovery.erl @@ -41,7 +41,7 @@ do(State, LibDirs, AppMeta) -> rlx_log:info(rlx_state:log(State), fun() -> ["Resolving available OTP Releases from directories:\n", - [[rlx_util:indent(2), LibDir, "\n"] || LibDir <- LibDirs]] + string:join([[rlx_util:indent(2), LibDir] || LibDir <- LibDirs], "\n")] end), resolve_rel_metadata(State, LibDirs, AppMeta). diff --git a/test/rlx_discover_SUITE.erl b/test/rlx_discover_SUITE.erl index 93a576b..e75ea36 100644 --- a/test/rlx_discover_SUITE.erl +++ b/test/rlx_discover_SUITE.erl @@ -138,12 +138,11 @@ bad_ebin_case(Config) -> Filename = filename:join([AppDir, <<"ebin">>, BadName ++ ".app"]), ok = filelib:ensure_dir(Filename), ok = ec_file:write_term(Filename, get_bad_app_metadata(BadName, BadVsn)), - write_beam_file(AppDir, BadName), State0 = proplists:get_value(state, Config), {DiscoverProvider, {ok, State1}} = rlx_provider:new(rlx_prv_discover, State0), - ?assertMatch({error, {_, [{invalid_app_file, Filename}]}}, - rlx_provider:do(DiscoverProvider, State1)). - + {ok, State2} = rlx_provider:do(DiscoverProvider, State1), + ?assertMatch([], [App || App <- rlx_state:available_apps(State2), + BadName =:= rlx_app_info:name(App)]). %%%=================================================================== %%% Helper functions @@ -178,7 +177,7 @@ get_bad_app_metadata(Name, Vsn) -> ["{application, ", Name, ", [{description, \"\"}, {vsn, \"", Vsn, "\"}, - {modules, [], + {modules, [missing], {applications, [kernel, stdlib]}]}."]. diff --git a/test/rlx_release_SUITE.erl b/test/rlx_release_SUITE.erl index 0684917..7ac95bc 100644 --- a/test/rlx_release_SUITE.erl +++ b/test/rlx_release_SUITE.erl @@ -94,7 +94,7 @@ make_release(Config) -> goal_app_2]}]), OutputDir = filename:join([proplists:get_value(data_dir, Config), create_random_name("relx-output")]), - {ok, State} = relx:do(undefined, undefined, [], [LibDir1], 2, + {ok, State} = relx:do(undefined, undefined, [], [LibDir1], 3, OutputDir, ConfigFile), [{{foo, "0.0.1"}, Release}] = ec_dictionary:to_list(rlx_state:realized_releases(State)), AppSpecs = rlx_release:applications(Release), @@ -130,7 +130,7 @@ make_invalid_config_release(Config) -> OutputDir = filename:join([proplists:get_value(data_dir, Config), create_random_name("relx-output")]), {error, {rlx_prv_config, - {consult, _, _}}} = relx:do(undefined, undefined, [], [LibDir1], 2, + {consult, _, _}}} = relx:do(undefined, undefined, [], [LibDir1], 3, OutputDir, ConfigFile). make_scriptless_release(Config) -> @@ -157,7 +157,7 @@ make_scriptless_release(Config) -> goal_app_2]}]), OutputDir = filename:join([proplists:get_value(data_dir, Config), create_random_name("relx-output")]), - {ok, State} = relx:do(undefined, undefined, [], [LibDir1], 2, + {ok, State} = relx:do(undefined, undefined, [], [LibDir1], 3, OutputDir, ConfigFile), ?assert(not ec_file:exists(filename:join([OutputDir, "bin", "foo"]))), @@ -207,7 +207,7 @@ make_overridden_release(Config) -> OutputDir = filename:join([proplists:get_value(data_dir, Config), create_random_name("relx-output")]), {ok, Cwd} = file:get_cwd(), - {ok, State} = relx:do(Cwd, undefined, undefined, [], [LibDir1], 2, + {ok, State} = relx:do(Cwd, undefined, undefined, [], [LibDir1], 3, OutputDir, [{OverrideAppName, OverrideAppDir}], ConfigFile), [{{foo, "0.0.1"}, Release}] = ec_dictionary:to_list(rlx_state:realized_releases(State)), @@ -248,7 +248,7 @@ make_skip_app_release(Config) -> OutputDir = filename:join([proplists:get_value(data_dir, Config), create_random_name("relx-output")]), {ok, Cwd} = file:get_cwd(), - {ok, State} = relx:do(Cwd, undefined, undefined, [], [LibDir1], 2, + {ok, State} = relx:do(Cwd, undefined, undefined, [], [LibDir1], 3, OutputDir, [], ConfigFile), [{{foo, "0.0.1"}, Release}] = ec_dictionary:to_list(rlx_state:realized_releases(State)), @@ -292,7 +292,7 @@ make_auto_skip_empty_app_release(Config) -> OutputDir = filename:join([proplists:get_value(data_dir, Config), create_random_name("relx-output")]), {ok, Cwd} = file:get_cwd(), - {ok, State} = relx:do(Cwd, undefined, undefined, [], [LibDir1], 2, + {ok, State} = relx:do(Cwd, undefined, undefined, [], [LibDir1], 3, OutputDir, [], ConfigFile), [{{foo, "0.0.1"}, Release}] = ec_dictionary:to_list(rlx_state:realized_releases(State)), @@ -330,7 +330,7 @@ make_app_type_none_release(Config) -> OutputDir = filename:join([proplists:get_value(data_dir, Config), create_random_name("relx-output")]), {ok, Cwd} = file:get_cwd(), - {ok, State} = relx:do(Cwd, undefined, undefined, [], [LibDir1], 2, + {ok, State} = relx:do(Cwd, undefined, undefined, [], [LibDir1], 3, OutputDir, [], ConfigFile), [{{foo, "0.0.1"}, Release}] = ec_dictionary:to_list(rlx_state:realized_releases(State)), @@ -370,7 +370,7 @@ make_implicit_config_release(Config) -> create_random_name("relx-output")]), ok = file:set_cwd(FooRoot), {ok, FooRoot} = file:get_cwd(), - {ok, State} = relx:do(undefined, undefined, [], [LibDir1], 2, + {ok, State} = relx:do(undefined, undefined, [], [LibDir1], 3, OutputDir, undefined), [{{foo, "0.0.1"}, Release}] = ec_dictionary:to_list(rlx_state:realized_releases(State)), ?assert(ec_file:exists(OutputDir)), @@ -417,12 +417,12 @@ make_rerun_overridden_release(Config) -> OutputDir = filename:join([proplists:get_value(data_dir, Config), create_random_name("relx-output")]), {ok, Cwd} = file:get_cwd(), - {ok, _} = relx:do(Cwd, undefined, undefined, [], [LibDir1], 2, + {ok, _} = relx:do(Cwd, undefined, undefined, [], [LibDir1], 3, OutputDir, [{OverrideAppName, OverrideAppDir}], ConfigFile), %% Now we run it again to see if it fails. - {ok, State} = relx:do(Cwd,undefined, undefined, [], [LibDir1], 2, + {ok, State} = relx:do(Cwd,undefined, undefined, [], [LibDir1], 3, OutputDir, [{OverrideAppName, OverrideAppDir}], ConfigFile), @@ -496,7 +496,7 @@ overlay_release(Config) -> OutputDir = filename:join([proplists:get_value(data_dir, Config), create_random_name("relx-output")]), - {ok, State} = relx:do(undefined, undefined, [], [LibDir1], 2, + {ok, State} = relx:do(undefined, undefined, [], [LibDir1], 3, OutputDir, ConfigFile), [{{foo, "0.0.1"}, Release}] = ec_dictionary:to_list(rlx_state:realized_releases(State)), @@ -554,7 +554,7 @@ overlay_release(Config) -> proplists:get_value(lib_dep_1_library, TemplateData)), ?assertEqual("false", proplists:get_value(lib_dep_1_link, TemplateData)), - ?assertEqual("(2:debug)", + ?assertEqual("(3:debug)", proplists:get_value(log, TemplateData)), ?assertEqual(OutputDir, proplists:get_value(output_dir, TemplateData)), @@ -609,7 +609,7 @@ make_goalless_release(Config) -> OutputDir = filename:join([proplists:get_value(data_dir, Config), create_random_name("relx-output")]), ?assertMatch({error,{rlx_prv_release,no_goals_specified}}, - relx:do(undefined, undefined, [], [LibDir1], 2, + relx:do(undefined, undefined, [], [LibDir1], 3, OutputDir, ConfigFile)). make_depfree_release(Config) -> @@ -634,7 +634,7 @@ make_depfree_release(Config) -> [goal_app_1]}]), OutputDir = filename:join([proplists:get_value(data_dir, Config), create_random_name("relx-output")]), - {ok, State} = relx:do(undefined, undefined, [], [LibDir1], 2, + {ok, State} = relx:do(undefined, undefined, [], [LibDir1], 3, OutputDir, ConfigFile), [{{foo, "0.0.1"}, Release}] = ec_dictionary:to_list(rlx_state:realized_releases(State)), AppSpecs = rlx_release:applications(Release), @@ -680,15 +680,15 @@ make_relup_release(Config) -> {goal_app_2, "0.0.3"}]}]), OutputDir = filename:join([proplists:get_value(data_dir, Config), create_random_name("relx-output")]), - {ok, _} = relx:do(foo, "0.0.1", [], [LibDir1], 2, + {ok, _} = relx:do(foo, "0.0.1", [], [LibDir1], 3, OutputDir, ConfigFile), - {ok, _} = relx:do(foo, "0.0.2", [], [LibDir1], 2, + {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, 2}, + {log_level, 3}, {output_dir, OutputDir}, {config, ConfigFile}], ["release", "relup"]), @@ -760,16 +760,16 @@ make_relup_release2(Config) -> {goal_app_2, "0.0.3"}]}]), OutputDir = filename:join([proplists:get_value(data_dir, Config), create_random_name("relx-output")]), - {ok, _} = relx:do(foo, "0.0.1", [], [LibDir1], 2, + {ok, _} = relx:do(foo, "0.0.1", [], [LibDir1], 3, OutputDir, ConfigFile), - {ok, _} = relx:do(foo, "0.0.2", [], [LibDir1], 2, + {ok, _} = relx:do(foo, "0.0.2", [], [LibDir1], 3, OutputDir, ConfigFile), {ok, State} = relx:do([{relname, foo}, {relvsn, "0.0.3"}, {upfrom, "0.0.1"}, {goals, []}, {lib_dirs, [LibDir1]}, - {log_level, 2}, + {log_level, 3}, {output_dir, OutputDir}, {config, ConfigFile}], ["release", "relup"]), @@ -816,7 +816,7 @@ make_one_app_top_level_release(Config) -> {ok, Cwd} = file:get_cwd(), ok = file:set_cwd(AppDir), - {ok, State} = relx:do(undefined, undefined, [], [], 2, + {ok, State} = relx:do(undefined, undefined, [], [], 3, OutputDir, ConfigFile), ok = file:set_cwd(Cwd), [{{foo, "0.0.1"}, Release}] = ec_dictionary:to_list(rlx_state:realized_releases(State)), |