diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/relx.erl | 27 | ||||
-rw-r--r-- | src/rlx_app_discovery.erl | 49 | ||||
-rw-r--r-- | src/rlx_cmd_args.erl | 38 | ||||
-rw-r--r-- | src/rlx_config.erl | 64 | ||||
-rw-r--r-- | src/rlx_prv_app_discover.erl (renamed from src/rlx_prv_discover.erl) | 67 | ||||
-rw-r--r-- | src/rlx_prv_archive.erl | 56 | ||||
-rw-r--r-- | src/rlx_prv_assembler.erl | 121 | ||||
-rw-r--r-- | src/rlx_prv_overlay.erl | 118 | ||||
-rw-r--r-- | src/rlx_prv_rel_discover.erl | 92 | ||||
-rw-r--r-- | src/rlx_prv_release.erl | 120 | ||||
-rw-r--r-- | src/rlx_prv_relup.erl | 25 | ||||
-rw-r--r-- | src/rlx_rel_discovery.erl | 4 | ||||
-rw-r--r-- | src/rlx_release.erl | 14 | ||||
-rw-r--r-- | src/rlx_state.erl | 28 | ||||
-rw-r--r-- | src/rlx_topo.erl | 6 |
15 files changed, 544 insertions, 285 deletions
diff --git a/src/relx.erl b/src/relx.erl index 25f3220..eb3b969 100644 --- a/src/relx.erl +++ b/src/relx.erl @@ -26,7 +26,7 @@ do/7, do/8, do/9, - format_error/2, + format_error/1, opt_spec_list/0]). -export_type([error/0]). @@ -216,18 +216,18 @@ opt_spec_list() -> {version, undefined, "version", undefined, "Print relx version"}, {root_dir, $r, "root", string, "The project root directory"}]. --spec format_error(Reason::term(), rlx_state:t()) -> string(). -format_error({invalid_return_value, Provider, Value}, _) -> +-spec format_error(Reason::term()) -> string(). +format_error({invalid_return_value, Provider, 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}}, _) -> +format_error({opt_parse, {invalid_option, Opt}}) -> io_lib:format("invalid option ~s~n", [Opt]); -format_error({opt_parse, Arg}, _) -> +format_error({opt_parse, Arg}) -> io_lib:format("~p~n", [Arg]); -format_error({error, {relx, Reason}}, State) -> - format_error(Reason, State); -format_error({error, {Module, Reason}}, State) -> - io_lib:format("~s~n", [Module:format_error(Reason, State)]). +format_error({error, {relx, Reason}}) -> + format_error(Reason); +format_error({error, {Module, Reason}}) -> + io_lib:format("~s~n", [Module:format_error(Reason)]). %%============================================================================ %% internal api @@ -298,19 +298,20 @@ run_provider(_ProviderName, Error) -> -spec usage() -> ok. usage() -> - getopt:usage(opt_spec_list(), "relx", "[*release-specification-file*]"). + getopt:usage(opt_spec_list(), "relx", "[<task>]"), + io:format("Several tasks are available:~n~nrelease\t\tCreate release.~nrelup\t\tGenerate a release upgrade.~ntar\t\tCreate tarball archive of a release.~n~n"). -spec report_error(rlx_state:t(), error()) -> none() | error(). report_error(State, Error) -> case Error of {error, {relx, {opt_parse, _}}} -> - io:format(standard_error, format_error(Error, State), []), + io:format(standard_error, format_error(Error), []), usage(); {error, {rlx_cmd_args, _}} -> - io:format(standard_error, format_error(Error, State), []), + io:format(standard_error, format_error(Error), []), usage(); _ -> - io:format(standard_error, format_error(Error, State), []) + io:format(standard_error, format_error(Error), []) end, case rlx_state:caller(State) of command_line -> diff --git a/src/rlx_app_discovery.erl b/src/rlx_app_discovery.erl index 2c5714b..3d58185 100644 --- a/src/rlx_app_discovery.erl +++ b/src/rlx_app_discovery.erl @@ -25,7 +25,7 @@ -module(rlx_app_discovery). -export([do/2, - format_error/2]). + format_error/1]). -include("relx.hrl"). @@ -44,10 +44,10 @@ do(State, LibDirs) -> end), resolve_app_metadata(State, LibDirs). --spec format_error([ErrorDetail::term()], rlx_state:t()) -> iolist(). -format_error(ErrorDetails, State) +-spec format_error([ErrorDetail::term()]) -> iolist(). +format_error(ErrorDetails) when erlang:is_list(ErrorDetails) -> - [[format_detail(ErrorDetail, State), "\n"] || ErrorDetail <- ErrorDetails]. + [[format_detail(ErrorDetail), "\n"] || ErrorDetail <- ErrorDetails]. %%%=================================================================== %%% Internal Functions @@ -85,10 +85,10 @@ get_app_metadata(State, LibDirs) -> {ok, _} = AppMeta -> [AppMeta|Acc]; {warning, W} -> - ec_cmd_log:warn(rlx_state:log(State), format_detail(W, State)), + ec_cmd_log:warn(rlx_state:log(State), format_detail(W)), Acc; {error, E} -> - ec_cmd_log:error(rlx_state:log(State), format_detail(E, State)), + ec_cmd_log:error(rlx_state:log(State), format_detail(E)), Acc; _ -> Acc @@ -111,15 +111,17 @@ resolve_app_metadata(State, LibDirs) -> {error, _} -> true; {warning, W} -> - ec_cmd_log:warn(rlx_state:log(State), format_detail(W, State)), + ec_cmd_log:warn(rlx_state:log(State), format_detail(W)), false; _ -> false end] of [] -> SkipApps = rlx_state:skip_apps(State), - AppMeta1 = [App || {ok, App} <- setup_overrides(State, AppMeta0), - not lists:keymember(rlx_app_info:name(App), 1, SkipApps)], + ExcludeApps = rlx_state:exclude_apps(State), + AppMeta1 = [rm_exclude_apps(App, ExcludeApps) || + {ok, App} <- setup_overrides(State, AppMeta0), + not lists:keymember(rlx_app_info:name(App), 1, SkipApps++ExcludeApps)], ec_cmd_log:debug(rlx_state:log(State), fun() -> ["Resolved the following OTP Applications from the system: \n", @@ -129,6 +131,11 @@ resolve_app_metadata(State, LibDirs) -> Errors -> ?RLX_ERROR(Errors) end. +%% Apps listed in {exclude_apps, [...]} must be removed from applications lists +rm_exclude_apps(App, ExcludeApps) -> + ActiveApps = lists:subtract(rlx_app_info:active_deps(App), ExcludeApps), + LibraryApps = lists:subtract(rlx_app_info:library_deps(App), ExcludeApps), + rlx_app_info:library_deps(rlx_app_info:active_deps(App, ActiveApps), LibraryApps). app_name({warning, _}) -> undefined; @@ -155,30 +162,30 @@ resolve_override(AppName, FileName0) -> {ok, rlx_app_info:link(App, true)} end. --spec format_detail(ErrorDetail::term(), rlx_state:t()) -> iolist(). -format_detail({missing_beam_file, Module, BeamFile}, _) -> +-spec format_detail(ErrorDetail::term()) -> iolist(). +format_detail({missing_beam_file, Module, BeamFile}) -> io_lib:format("Missing beam file ~p ~p", [Module, BeamFile]); -format_detail({error, {invalid_override, AppName, FileName}}, _) -> +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?", [AppName, FileName]); -format_detail({accessing, File, eaccess}, _) -> +format_detail({accessing, File, eaccess}) -> io_lib:format("permission denied accessing file ~s", [File]); -format_detail({accessing, File, Type}, _) -> +format_detail({accessing, File, Type}) -> io_lib:format("error (~p) accessing file ~s", [Type, File]); -format_detail({no_beam_files, EbinDir}, _) -> +format_detail({no_beam_files, EbinDir}) -> io_lib:format("no beam files found in directory ~s", [EbinDir]); -format_detail({not_a_directory, EbinDir}, _) -> +format_detail({not_a_directory, EbinDir}) -> io_lib:format("~s is not a directory when it should be a directory", [EbinDir]); -format_detail({unable_to_load_app, AppDir, _}, _) -> +format_detail({unable_to_load_app, AppDir, _}) -> io_lib:format("Unable to load the application metadata from ~s", [AppDir]); -format_detail({invalid_app_file, File}, _) -> +format_detail({invalid_app_file, File}) -> io_lib:format("Application metadata file exists but is malformed: ~s", [File]); -format_detail({unversioned_app, AppDir, _AppName}, _) -> +format_detail({unversioned_app, AppDir, _AppName}) -> io_lib:format("Application metadata exists but version is not available: ~s", [AppDir]); -format_detail({app_info_error, {Module, Detail}}, State) -> - Module:format_error(Detail, State). +format_detail({app_info_error, {Module, Detail}}) -> + Module:format_error(Detail). -spec discover_dir([file:name()], directory | file) -> {ok, rlx_app_info:t()} | {error, Reason::term()}. diff --git a/src/rlx_cmd_args.erl b/src/rlx_cmd_args.erl index a47c2ee..2039b43 100644 --- a/src/rlx_cmd_args.erl +++ b/src/rlx_cmd_args.erl @@ -22,7 +22,7 @@ -module(rlx_cmd_args). -export([args2state/2, - format_error/2]). + format_error/1]). -include("relx.hrl"). @@ -48,10 +48,10 @@ args2state(Opts, Targets) -> Error end. --spec format_error(Reason::term(), rlx_state:t()) -> iolist(). -format_error({invalid_targets, Targets}, _) -> +-spec format_error(Reason::term()) -> iolist(). +format_error({invalid_targets, Targets}) -> io_lib:format("One config must be specified! not ~p~n", [Targets]); -format_error({invalid_option_arg, Arg}, _) -> +format_error({invalid_option_arg, Arg}) -> case Arg of {goals, Goal} -> io_lib:format("Invalid Goal argument -g ~p~n", [Goal]); @@ -68,20 +68,20 @@ format_error({invalid_option_arg, Arg}, _) -> {path, Path} -> io_lib:format("Invalid code path argument -n ~p~n", [Path]) end; -format_error({invalid_config_file, Config}, _) -> +format_error({invalid_config_file, Config}) -> io_lib:format("Invalid configuration file specified: ~p", [Config]); -format_error({invalid_caller, Caller}, _) -> +format_error({invalid_caller, Caller}) -> io_lib:format("Invalid caller specified: ~s", [Caller]); -format_error({failed_to_parse, Spec}, _) -> +format_error({failed_to_parse, Spec}) -> io_lib:format("Unable to parse spec ~s", [Spec]); -format_error({failed_to_parse_override, QA}, _) -> +format_error({failed_to_parse_override, QA}) -> io_lib:format("Failed to parse app override ~s", [QA]); -format_error({not_directory, Dir}, _) -> +format_error({not_directory, Dir}) -> io_lib:format("Library directory does not exist: ~s", [Dir]); -format_error({invalid_log_level, LogLevel}, _) -> +format_error({invalid_log_level, LogLevel}) -> io_lib:format("Invalid log level specified -V ~p, log level must be in the" " range 0..3", [LogLevel]); -format_error({invalid_target, Target}, _) -> +format_error({invalid_target, Target}) -> io_lib:format("Invalid action specified: ~s", [Target]). %%%=================================================================== @@ -102,7 +102,7 @@ handle_config(Opts, Targets, CommandLineConfig) -> end end. --spec convert_targets([string()]) -> {ok, release | relup} | relx:error(). +-spec convert_targets([string()]) -> {ok, [rlx_state:action()]} | relx:error(). convert_targets(Targets) -> convert_targets(Targets, []). @@ -111,7 +111,7 @@ convert_targets(Targets) -> convert_targets([], []) -> {ok, [release]}; convert_targets([], Acc) -> - {ok, Acc}; + {ok, lists:reverse(Acc)}; convert_targets(["release" | T], Acc) -> convert_targets(T, [release | Acc]); convert_targets(["relup" | T], Acc) -> @@ -244,8 +244,16 @@ create(vm_args, Opts) -> VmArgs = proplists:get_value(vm_args, Opts, undefined), {vm_args, VmArgs}; create(system_libs, Opts) -> - SystemLibs = proplists:get_value(system_libs, Opts, undefined), - {system_libs, SystemLibs}; + case proplists:get_value(system_libs, Opts, true) of + SystemLibs when SystemLibs =:= true + ; SystemLibs =:= "true" -> + {system_libs, true}; + SystemLibs when SystemLibs =:= false + ; SystemLibs =:= "false" -> + {system_libs, false}; + SystemLibsDir when is_list(SystemLibsDir) -> + {system_libs, SystemLibsDir} + end; create(upfrom, Opts) -> case proplists:get_value(upfrom, Opts, undefined) of undefined -> diff --git a/src/rlx_config.erl b/src/rlx_config.erl index c838c18..df08342 100644 --- a/src/rlx_config.erl +++ b/src/rlx_config.erl @@ -97,25 +97,50 @@ parent_dir([_H], Acc) -> parent_dir([H | T], Acc) -> parent_dir(T, [H | Acc]). +-spec config_script_file(file:filename(), rlx_state:t()) -> file:filename(). +config_script_file(ConfigFile, _State) -> + ConfigFile ++ ".script". + +bs(Vars) -> + lists:foldl(fun({K,V}, Bs) -> + erl_eval:add_binding(K, V, Bs) + end, erl_eval:new_bindings(), Vars). + +-spec apply_config_script(proplists:proplist(), file:filename()) -> + proplists:proplist(). +apply_config_script(ConfigData, ConfigScriptFile) -> + {ok, Config} = file:script(ConfigScriptFile, bs([{'CONFIG', ConfigData}, + {'SCRIPT', ConfigScriptFile}])), + Config. + -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(), - 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)) + CliTerms = rlx_state:cli_args(State), + Config0 = 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} -> merge_configs(CliTerms, Terms) + end, + ok = file:set_cwd(CurrentCwd), + Result; + false -> merge_configs(CliTerms, ConfigFile) + end, + % we now take the merged config and try to apply a config script to it, + % get a new config as a result + case Config0 of + {error, _} = Error -> Error; + _ -> + ConfigScriptFile = config_script_file(ConfigFile, State), + Config1 = case filelib:is_regular(ConfigScriptFile) of + false -> Config0; + true -> apply_config_script(Config0, ConfigScriptFile) + end, + lists:foldl(fun load_terms/2, {ok, State}, Config1) end. -spec load_terms(term(), {ok, rlx_state:t()} | relx:error()) -> @@ -131,11 +156,6 @@ load_terms({default_libs, DefaultLibs}, {ok, State}) -> default_libs, DefaultLibs), {ok, State2}; -load_terms({system_libs, SystemLibs}, {ok, State}) -> - State2 = rlx_state:put(State, - system_libs, - SystemLibs), - {ok, State2}; load_terms({lib_dirs, Dirs}, {ok, State}) -> State2 = rlx_state:add_lib_dirs(State, @@ -162,6 +182,10 @@ load_terms({add_providers, Providers0}, {ok, State0}) -> end; load_terms({skip_apps, SkipApps0}, {ok, State0}) -> {ok, rlx_state:skip_apps(State0, SkipApps0)}; +load_terms({exclude_apps, ExcludeApps0}, {ok, State0}) -> + {ok, rlx_state:exclude_apps(State0, ExcludeApps0)}; +load_terms({debug_info, DebugInfo}, {ok, State0}) -> + {ok, rlx_state:debug_info(State0, DebugInfo)}; load_terms({overrides, Overrides0}, {ok, State0}) -> {ok, rlx_state:overrides(State0, Overrides0)}; load_terms({dev_mode, DevMode}, {ok, State0}) -> diff --git a/src/rlx_prv_discover.erl b/src/rlx_prv_app_discover.erl index 41e3993..eb40f38 100644 --- a/src/rlx_prv_discover.erl +++ b/src/rlx_prv_app_discover.erl @@ -22,16 +22,17 @@ %%% 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 provider behaviour. --module(rlx_prv_discover). +-module(rlx_prv_app_discover). + -behaviour(provider). -export([init/1, do/1, - format_error/2]). + format_error/1]). -include("relx.hrl"). --define(PROVIDER, discover). +-define(PROVIDER, app_discover). -define(DEPS, []). %%============================================================================ @@ -42,48 +43,31 @@ init(State) -> State1 = rlx_state:add_provider(State, providers:create([{name, ?PROVIDER}, {module, ?MODULE}, - {bare, false}, - {deps, ?DEPS}, - {example, "build"}, - {short_desc, ""}, - {desc, ""}, - {opts, []}])), + {deps, ?DEPS}])), {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(State0) -> - LibDirs = get_lib_dirs(State0), + LibDirs = dedup(get_lib_dirs(State0)), case rlx_app_discovery:do(State0, LibDirs) of {ok, AppMeta} -> - case rlx_rel_discovery:do(State0, LibDirs, AppMeta) of - {ok, Releases} -> - State1 = rlx_state:available_apps(State0, AppMeta), - {ok, rlx_state:realized_releases(State1, lists:foldl(fun add/2, - ec_dictionary:new(ec_dict), - Releases))}; - Error -> - Error - end; + State1 = rlx_state:available_apps(State0, AppMeta), + {ok, State1}; Error -> Error end. %% @doc this is here to comply with the signature. However, we do not actually %% produce any errors and so simply return an empty string. --spec format_error(any(), rlx_state:t()) -> iolist(). -format_error(_, _) -> +-spec format_error(any()) -> iolist(). +format_error(_) -> "". %%%=================================================================== %%% Internal Functions %%%=================================================================== -%% @doc only add the release if its not documented in the system -add(Rel, Dict) -> - RelName = rlx_release:name(Rel), - RelVsn = rlx_release:vsn(Rel), - ec_dictionary:add({RelName, RelVsn}, Rel, Dict). get_lib_dirs(State) -> LibDirs0 = rlx_state:lib_dirs(State), @@ -91,10 +75,17 @@ get_lib_dirs(State) -> false -> LibDirs0; true -> + Output = erlang:iolist_to_binary(rlx_state:base_output_dir(State)), + OutputDir = case ec_file:exists(binary_to_list(Output)) of + true -> + Output; + false -> + [] + end, lists:flatten([LibDirs0, add_common_project_dirs(State), add_system_lib_dir(State), - add_release_output_dir(State)]) + OutputDir]) end. -spec add_common_project_dirs(rlx_state:t()) -> [file:name()]. @@ -124,8 +115,8 @@ add_common_project_dirs(State) -> -spec add_system_lib_dir(rlx_state:t()) -> [file:name()]. add_system_lib_dir(State) -> ExcludeSystem = rlx_state:get(State, discover_exclude_system, false), - case rlx_state:get(State, system_libs, undefined) of - undefined -> + case rlx_state:get(State, system_libs, true) of + Atom when is_atom(Atom) -> case ExcludeSystem of true -> []; @@ -136,16 +127,8 @@ add_system_lib_dir(State) -> erlang:iolist_to_binary(SystemLibs) end. -add_release_output_dir(State) -> - case rlx_state:get(State, disable_discover_release_output, false) of - true -> - []; - false -> - Output = erlang:iolist_to_binary(rlx_state:base_output_dir(State)), - case ec_file:exists(erlang:binary_to_list(Output)) of - true -> - Output; - false -> - [] - end - end. +%% Order matters so this slow dedup needs to be used +dedup([]) -> + []; +dedup([H|T]) -> + [H | [X || X <- dedup(T), X /= H]]. diff --git a/src/rlx_prv_archive.erl b/src/rlx_prv_archive.erl index e4cd18f..62cc37c 100644 --- a/src/rlx_prv_archive.erl +++ b/src/rlx_prv_archive.erl @@ -26,7 +26,7 @@ -export([init/1, do/1, - format_error/2]). + format_error/1]). -include("relx.hrl"). @@ -41,12 +41,7 @@ init(State) -> State1 = rlx_state:add_provider(State, providers:create([{name, ?PROVIDER}, {module, ?MODULE}, - {bare, false}, - {deps, ?DEPS}, - {example, "tar"}, - {short_desc, ""}, - {desc, ""}, - {opts, []}])), + {deps, ?DEPS}])), {ok, State1}. @@ -57,13 +52,13 @@ do(State) -> OutputDir = rlx_state:output_dir(State), make_tar(State, Release, OutputDir). -format_error({tar_unknown_generation_error, Module, Vsn}, _) -> +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}, _) -> +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}, _) -> +format_error({tar_generation_error, Module, Errors}) -> io_lib:format("Tarball generation error for ~p reason ~p", [Module, Errors]). @@ -72,7 +67,7 @@ make_tar(State, Release, OutputDir) -> Vsn = rlx_release:vsn(Release), ErtsVersion = rlx_release:erts(Release), Opts = [{path, [filename:join([OutputDir, "lib", "*", "ebin"])]}, - {outdir, OutputDir} | + {outdir, OutputDir} | case rlx_state:get(State, include_erts, true) of true -> Prefix = code:root_dir(), @@ -104,14 +99,18 @@ make_tar(State, Release, OutputDir) -> end. update_tar(State, TempDir, OutputDir, Name, Vsn, ErtsVersion) -> + IncludeErts = rlx_state:get(State, include_erts, true), + SystemLibs = rlx_state:get(State, system_libs, true), + {RelName, RelVsn} = rlx_state:default_configured_release(State), + Release = rlx_state:get_realized_release(State, RelName, RelVsn), TarFile = filename:join(OutputDir, Name++"-"++Vsn++".tar.gz"), file:rename(filename:join(OutputDir, Name++".tar.gz"), TarFile), erl_tar:extract(TarFile, [{cwd, TempDir}, compressed]), - OverlayFiles = overlay_files(rlx_state:get(State, overlay, undefined), OutputDir), + OverlayVars = rlx_prv_overlay:generate_overlay_vars(State, Release), + OverlayFiles = overlay_files(OverlayVars, rlx_state:get(State, overlay, undefined), OutputDir), ok = erl_tar:create(TarFile, - [{"lib", filename:join(TempDir, "lib")}, - {"releases", filename:join(TempDir, "releases")}, + [{"releases", filename:join(TempDir, "releases")}, {filename:join(["releases", "start_erl.data"]), filename:join([OutputDir, "releases", "start_erl.data"])}, {filename:join(["releases", "RELEASES"]), @@ -119,21 +118,36 @@ update_tar(State, TempDir, OutputDir, Name, Vsn, ErtsVersion) -> {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 + case IncludeErts of false -> - []; + %% Remove system libs from tarball + case SystemLibs of + false -> + Libs = filelib:wildcard("*", filename:join(TempDir, "lib")), + AllSystemLibs = filelib:wildcard("*", code:lib_dir()), + [{filename:join("lib", LibDir), filename:join([TempDir, "lib", LibDir])} || + LibDir <- lists:subtract(Libs, AllSystemLibs)]; + _ -> + [{"lib", filename:join(TempDir, "lib")}] + end; _ -> - [{"erts-"++ErtsVersion, filename:join(OutputDir, "erts-"++ErtsVersion)}] + [{"lib", filename:join(TempDir, "lib")}, + {"erts-"++ErtsVersion, filename:join(OutputDir, "erts-"++ErtsVersion)}] end]++OverlayFiles, [compressed]), ec_cmd_log:info(rlx_state:log(State), - "tarball ~s successfully created!~n", [TarFile]), + "tarball ~s successfully created!~n", [TarFile]), ec_file:remove(TempDir, [recursive]), {ok, State}. -overlay_files(undefined, _) -> +overlay_files(_, undefined, _) -> []; -overlay_files(Overlay, OutputDir) -> - [{to(O), filename:join(OutputDir, to(O))} || O <- Overlay, filter(O)]. +overlay_files(OverlayVars, Overlay, OutputDir) -> + [begin + To = to(O), + ToTemplateName = rlx_prv_overlay:make_template_name("rlx_template_to_template", To), + File = rlx_prv_overlay:render_string(OverlayVars, To, ToTemplateName), + {ec_cnv:to_list(File), ec_cnv:to_list(filename:join(OutputDir, File))} + end || O <- Overlay, filter(O)]. to({copy, _, To}) -> To; diff --git a/src/rlx_prv_assembler.erl b/src/rlx_prv_assembler.erl index c535485..60cef76 100644 --- a/src/rlx_prv_assembler.erl +++ b/src/rlx_prv_assembler.erl @@ -26,7 +26,7 @@ -export([init/1, do/1, - format_error/2]). + format_error/1]). -include("relx.hrl"). @@ -40,12 +40,7 @@ init(State) -> State1 = rlx_state:add_provider(State, providers:create([{name, ?PROVIDER}, {module, ?MODULE}, - {bare, false}, - {deps, ?DEPS}, - {example, "release"}, - {short_desc, ""}, - {desc, ""}, - {opts, []}])), + {deps, ?DEPS}])), {ok, State1}. %% @doc recursively dig down into the library directories specified in the state @@ -60,7 +55,23 @@ do(State) -> ok -> case rlx_release:realized(Release) of true -> - copy_app_directories_to_output(State, Release, OutputDir); + case copy_app_directories_to_output(State, Release, OutputDir) of + {ok, State1} -> + case rlx_state:debug_info(State1) =:= strip + andalso rlx_state:dev_mode(State1) =:= false of + true -> + case beam_lib:strip_release(OutputDir) of + {ok, _} -> + {ok, State1}; + {error, _, Reason} -> + ?RLX_ERROR({strip_release, Reason}) + end; + false -> + {ok, State1} + end; + E -> + E + end; false -> ?RLX_ERROR({unresolved_release, RelName, RelVsn}) end; @@ -68,34 +79,37 @@ do(State) -> Error end. --spec format_error(ErrorDetail::term(), rlx_state:t()) -> iolist(). -format_error({unresolved_release, RelName, RelVsn}, _) -> +-spec format_error(ErrorDetail::term()) -> iolist(). +format_error({unresolved_release, RelName, RelVsn}) -> io_lib:format("The release has not been resolved ~p-~s", [RelName, RelVsn]); -format_error({ec_file_error, AppDir, TargetDir, E}, _) -> +format_error({ec_file_error, AppDir, TargetDir, E}) -> io_lib:format("Unable to copy OTP App from ~s to ~s due to ~p", [AppDir, TargetDir, E]); -format_error({config_does_not_exist, Path}, _) -> +format_error({config_does_not_exist, Path}) -> io_lib:format("The config file specified for this release (~s) does not exist!", [Path]); -format_error({specified_erts_does_not_exist, ErtsVersion}, _) -> +format_error({specified_erts_does_not_exist, ErtsVersion}) -> io_lib:format("Specified version of erts (~s) does not exist", [ErtsVersion]); -format_error({release_script_generation_error, RelFile}, _) -> +format_error({release_script_generation_error, RelFile}) -> io_lib:format("Unknown internal release error generating the release file to ~s", [RelFile]); -format_error({release_script_generation_warning, Module, Warnings}, _) -> +format_error({release_script_generation_warning, Module, Warnings}) -> ["Warnings generating release \s", rlx_util:indent(2), Module:format_warning(Warnings)]; -format_error({unable_to_create_output_dir, OutputDir}, _) -> +format_error({unable_to_create_output_dir, OutputDir}) -> io_lib:format("Unable to create output directory (possible permissions issue): ~s", [OutputDir]); -format_error({release_script_generation_error, Module, Errors}, State) -> +format_error({release_script_generation_error, Module, Errors}) -> ["Errors generating release \n", - rlx_util:indent(2), Module:format_error(Errors, State)]; -format_error({unable_to_make_symlink, AppDir, TargetDir, Reason}, _) -> + 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)]). + file:format_error(Reason)]); +format_error({strip_release, Reason}) -> + io_lib:format("Stripping debug info from release beam files failed becuase ~s", + [beam_lib:format_error(Reason)]). %%%=================================================================== %%% Internal Functions @@ -128,6 +142,7 @@ copy_app_directories_to_output(State, Release, OutputDir) -> LibDir = filename:join([OutputDir, "lib"]), ok = ec_file:mkdir_p(LibDir), IncludeSrc = rlx_state:include_src(State), + IncludeErts = rlx_state:get(State, include_erts, true), Apps = prepare_applications(State, rlx_release:application_details(Release)), Result = lists:filter(fun({error, _}) -> true; @@ -135,7 +150,7 @@ copy_app_directories_to_output(State, Release, OutputDir) -> false end, lists:flatten(ec_plists:map(fun(App) -> - copy_app(LibDir, App, IncludeSrc) + copy_app(LibDir, App, IncludeSrc, IncludeErts) end, Apps))), case Result of [E | _] -> @@ -152,7 +167,7 @@ prepare_applications(State, Apps) -> Apps end. -copy_app(LibDir, App, IncludeSrc) -> +copy_app(LibDir, App, IncludeSrc, IncludeErts) -> AppName = erlang:atom_to_list(rlx_app_info:name(App)), AppVsn = rlx_app_info:original_vsn(App), AppDir = rlx_app_info:dir(App), @@ -163,16 +178,69 @@ copy_app(LibDir, App, IncludeSrc) -> %% a release dir ok; true -> - copy_app(App, AppDir, TargetDir, IncludeSrc) + case IncludeErts of + false -> + case is_erts_lib(AppDir) of + true -> + []; + false -> + copy_app_(App, AppDir, TargetDir, IncludeSrc) + end; + _ -> + copy_app_(App, AppDir, TargetDir, IncludeSrc) + end end. -copy_app(App, AppDir, TargetDir, IncludeSrc) -> +is_erts_lib(Dir) -> + lists:prefix(filename:split(list_to_binary(code:lib_dir())), filename:split(Dir)). + +copy_app_(App, AppDir, TargetDir, IncludeSrc) -> remove_symlink_or_directory(TargetDir), case rlx_app_info:link(App) of true -> - link_directory(AppDir, TargetDir); + link_directory(AppDir, TargetDir), + rewrite_app_file(App, AppDir); false -> - copy_directory(AppDir, TargetDir, IncludeSrc) + copy_directory(AppDir, TargetDir, IncludeSrc), + rewrite_app_file(App, TargetDir) + end. + +%% If excluded apps exist in this App's applications list we must write a new .app +rewrite_app_file(App, TargetDir) -> + Name = rlx_app_info:name(App), + ActiveDeps = rlx_app_info:active_deps(App), + IncludedDeps = rlx_app_info:library_deps(App), + AppFile = filename:join([TargetDir, "ebin", ec_cnv:to_list(Name) ++ ".app"]), + + {ok, [{application, AppName, AppData}]} = file:consult(AppFile), + OldActiveDeps = proplists:get_value(applications, AppData, []), + OldIncludedDeps = proplists:get_value(included_applications, AppData, []), + + case {OldActiveDeps, OldIncludedDeps} of + {ActiveDeps, IncludedDeps} -> + ok; + _ -> + AppData1 = lists:keyreplace(applications + ,1 + ,AppData + ,{applications, ActiveDeps}), + AppData2 = lists:keyreplace(included_applications + ,1 + ,AppData1 + ,{included_applications, IncludedDeps}), + Spec = io_lib:format("~p.\n", [{application, AppName, AppData2}]), + write_file_if_contents_differ(AppFile, Spec) + end. + +write_file_if_contents_differ(Filename, Bytes) -> + ToWrite = iolist_to_binary(Bytes), + case file:read_file(Filename) of + {ok, ToWrite} -> + ok; + {ok, _} -> + file:write_file(Filename, ToWrite); + {error, _} -> + file:write_file(Filename, ToWrite) end. remove_symlink_or_directory(TargetDir) -> @@ -424,6 +492,7 @@ include_erts(State, Release, OutputDir, RelDir) -> make_boot_script(State, Release, OutputDir, RelDir) -> Options = [{path, [RelDir | rlx_util:get_code_paths(Release, OutputDir)]}, {outdir, RelDir}, + {variables, [{"ERTS_LIB_DIR", code:lib_dir()}]}, no_module_tests, silent], Name = erlang:atom_to_list(rlx_release:name(Release)), ReleaseFile = filename:join([RelDir, Name ++ ".rel"]), diff --git a/src/rlx_prv_overlay.erl b/src/rlx_prv_overlay.erl index 6df142b..cdce915 100644 --- a/src/rlx_prv_overlay.erl +++ b/src/rlx_prv_overlay.erl @@ -26,7 +26,11 @@ -export([init/1, do/1, - format_error/2]). + format_error/1]). + +-export([generate_overlay_vars/2, + make_template_name/2, + render_string/3]). -define(DIRECTORY_RE, ".*(\/|\\\\)$"). @@ -45,12 +49,7 @@ init(State) -> State1 = rlx_state:add_provider(State, providers:create([{name, ?PROVIDER}, {module, ?MODULE}, - {bare, false}, - {deps, ?DEPS}, - {example, "overlay"}, - {short_desc, ""}, - {desc, ""}, - {opts, []}])), + {deps, ?DEPS}])), {ok, State1}. %% @doc recursively dig down into the library directories specified in the state @@ -61,45 +60,50 @@ do(State) -> Release = rlx_state:get_realized_release(State, RelName, RelVsn), case rlx_release:realized(Release) of true -> - generate_overlay_vars(State, Release); + case generate_overlay_vars(State, Release) of + {error, Reason} -> + {error, Reason}; + OverlayVars -> + do_overlay(State, OverlayVars) + end; false -> ?RLX_ERROR({unresolved_release, RelName, RelVsn}) end. --spec format_error(ErrorDetail::term(), rlx_state:t()) -> iolist(). -format_error({unresolved_release, RelName, RelVsn}, _) -> +-spec format_error(ErrorDetail::term()) -> iolist(). +format_error({unresolved_release, RelName, RelVsn}) -> io_lib:format("The release has not been resolved ~p-~s", [RelName, RelVsn]); -format_error({ec_file_error, AppDir, TargetDir, E}, _) -> +format_error({ec_file_error, AppDir, TargetDir, E}) -> io_lib:format("Unable to copy OTP App from ~s to ~s due to ~p", [AppDir, TargetDir, E]); -format_error({unable_to_read_varsfile, FileName, Reason}, _) -> +format_error({unable_to_read_varsfile, FileName, Reason}) -> io_lib:format("Unable to read vars file (~s) for overlay due to: ~p", [FileName, Reason]); -format_error({overlay_failed, Errors}, State) -> - [[format_error(rlx_util:error_reason(Error), State), "\n"] || Error <- Errors]; -format_error({dir_render_failed, Dir, Error}, _) -> +format_error({overlay_failed, Errors}) -> + [[format_error(rlx_util:error_reason(Error)), "\n"] || Error <- Errors]; +format_error({dir_render_failed, Dir, Error}) -> io_lib:format("rendering mkdir path failed ~s with ~p", [Dir, Error]); -format_error({unable_to_make_symlink, AppDir, TargetDir, Reason}, _) -> +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({copy_failed, FromFile, ToFile, Err}, _) -> +format_error({copy_failed, FromFile, ToFile, Err}) -> io_lib:format("Unable to copy from ~s to ~s because of ~p", [FromFile, ToFile, Err]); -format_error({unable_to_write, ToFile, Reason}, _) -> +format_error({unable_to_write, ToFile, Reason}) -> io_lib:format("Unable to write to ~s because ~p", [ToFile, Reason]); -format_error({unable_to_enclosing_dir, ToFile, Reason}, _) -> +format_error({unable_to_enclosing_dir, ToFile, Reason}) -> io_lib:format("Unable to create enclosing directory for ~s because ~p", [ToFile, Reason]); -format_error({unable_to_render_template, FromFile, Reason}, _) -> +format_error({unable_to_render_template, FromFile, Reason}) -> io_lib:format("Unable to render template ~s because ~p", [FromFile, Reason]); -format_error({unable_to_compile_template, FromFile, Reason}, State) -> +format_error({unable_to_compile_template, FromFile, Reason}) -> io_lib:format("Unable to compile template ~s because \n~s", - [FromFile, [format_errors(F, Es, State) || {F, Es} <- Reason]]); -format_error({unable_to_make_dir, Absolute, Error}, _) -> + [FromFile, [format_errors(F, Es) || {F, Es} <- Reason]]); +format_error({unable_to_make_dir, Absolute, Error}) -> io_lib:format("Unable to make directory ~s because ~p", [Absolute, Error]). @@ -107,39 +111,39 @@ format_error({unable_to_make_dir, Absolute, Error}, _) -> %%% Internal Functions %%%=================================================================== -format_errors(File, [{none, Mod, E}|Es], State) -> +format_errors(File, [{none, Mod, E}|Es]) -> [io_lib:format("~s~s: ~ts~n", [rlx_util:indent(2), File, - Mod:format_error(E, State)]) - |format_errors(File, Es, State)]; -format_errors(File, [{{Line, Col}, Mod, E}|Es], State) -> + Mod:format_error(E)]) + |format_errors(File, Es)]; +format_errors(File, [{{Line, Col}, Mod, E}|Es]) -> [io_lib:format("~s~s:~w:~w: ~ts~n", [rlx_util:indent(2), File, Line, Col, - Mod:format_error(E, State)]) - |format_errors(File, Es, State)]; -format_errors(File, [{Line, Mod, E}|Es], State) -> + Mod:format_error(E)]) + |format_errors(File, Es)]; +format_errors(File, [{Line, Mod, E}|Es]) -> [io_lib:format("~s~s:~w: ~ts~n", [rlx_util:indent(2), File, Line, - Mod:format_error(E, State)]) - |format_errors(File, Es, State)]; -format_errors(_, [], _State) -> []. + Mod:format_error(E)]) + |format_errors(File, Es)]; +format_errors(_, []) -> []. -spec generate_overlay_vars(rlx_state:t(), rlx_release:t()) -> - {ok, rlx_state:t()} | relx:error(). + proplists:proplist() | relx:error(). generate_overlay_vars(State, Release) -> StateVars = generate_state_vars(State), ReleaseVars = generate_release_vars(Release), get_overlay_vars_from_file(State, StateVars ++ ReleaseVars). -spec get_overlay_vars_from_file(rlx_state:t(), proplists:proplist()) -> - {ok, rlx_state:t()} | relx:error(). + proplists:proplist() | relx:error(). get_overlay_vars_from_file(State, OverlayVars) -> case rlx_state:get(State, overlay_vars, undefined) of undefined -> - do_overlay(State, OverlayVars); + OverlayVars; [] -> - do_overlay(State, OverlayVars); + OverlayVars; [H | _]=FileNames when is_list(H) -> read_overlay_vars(State, OverlayVars, FileNames); FileName when is_list(FileName) -> @@ -147,16 +151,31 @@ get_overlay_vars_from_file(State, OverlayVars) -> end. -spec read_overlay_vars(rlx_state:t(), proplists:proplist(), [file:name()]) -> - {ok, rlx_state:t()} | relx:error(). + proplists:proplist() | relx:error(). read_overlay_vars(State, OverlayVars, FileNames) -> Terms = merge_overlay_vars(State, FileNames), case render_overlay_vars(OverlayVars, Terms, []) of {ok, NewTerms} -> - do_overlay(State, OverlayVars ++ NewTerms); + OverlayVars ++ NewTerms; Error -> Error end. +-spec check_overlay_inclusion(rlx_state:t(), string(), proplists:proplist()) -> + proplists:proplist(). +check_overlay_inclusion(State, RelativeRoot, Terms) -> + check_overlay_inclusion(State, RelativeRoot, Terms, []). + +-spec check_overlay_inclusion(rlx_state:t(), string(), proplists:proplist(), proplists:proplist()) -> + proplists:proplist(). +check_overlay_inclusion(State, RelativeRoot, [File|T], Terms) when is_list(File) -> + IncludedTerms = merge_overlay_vars(State, [filename:join(RelativeRoot, File)]), + check_overlay_inclusion(State, RelativeRoot, T, Terms ++ IncludedTerms); +check_overlay_inclusion(State, RelativeRoot, [Tuple|T], Terms) -> + check_overlay_inclusion(State, RelativeRoot, T, Terms ++ [Tuple]); +check_overlay_inclusion(_State, _RelativeRoot, [], Terms) -> + Terms. + -spec merge_overlay_vars(rlx_state:t(), [file:name()]) -> proplists:proplist(). merge_overlay_vars(State, FileNames) -> @@ -165,10 +184,14 @@ merge_overlay_vars(State, FileNames) -> RelativePath = filename:join(RelativeRoot, erlang:iolist_to_binary(FileName)), case file:consult(RelativePath) of {ok, Terms} -> - lists:ukeymerge(1, lists:ukeysort(1, Terms), Acc); + % the location of the included overlay files will be relative + %% to the current one being read + OverlayRelativeRoot = filename:dirname(FileName), + NewTerms = check_overlay_inclusion(State, OverlayRelativeRoot, Terms), + lists:ukeymerge(1, lists:ukeysort(1, NewTerms), Acc); {error, Reason} -> ec_cmd_log:warn(rlx_state:log(State), - format_error({unable_to_read_varsfile, FileName, Reason}, State)), + format_error({unable_to_read_varsfile, FileName, Reason})), Acc end end, [], FileNames). @@ -431,6 +454,19 @@ write_template(OverlayVars, FromFile, ToFile) -> Error end. +render_string(OverlayVars, Data, TemplateName) -> + case erlydtl:compile(erlang:iolist_to_binary(Data), TemplateName, ?ERLYDTL_COMPILE_OPTS) of + {ok, TemplateName} -> + case render(TemplateName, OverlayVars) of + {ok, IoList} -> + erlang:iolist_to_binary(IoList); + {error, Error} -> + ?RLX_ERROR({render_failed, Data, Error}) + end; + {error, Reason, _Warnings} -> + ?RLX_ERROR({unable_to_compile_template, Data, Reason}) + end. + -spec file_render_do(proplists:proplist(), iolist(), module(), fun((term()) -> {ok, rlx_state:t()} | relx:error())) -> {ok, rlx_state:t()} | relx:error(). diff --git a/src/rlx_prv_rel_discover.erl b/src/rlx_prv_rel_discover.erl new file mode 100644 index 0000000..4224e75 --- /dev/null +++ b/src/rlx_prv_rel_discover.erl @@ -0,0 +1,92 @@ +%% -*- 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 (C) 2012 Erlware, LLC. +%%% +-module(rlx_prv_rel_discover). +-behaviour(provider). + +-export([init/1, + do/1, + format_error/1]). + +-include("relx.hrl"). + +-define(PROVIDER, rel_discover). +-define(DEPS, [app_discover]). + +%%============================================================================ +%% API +%%============================================================================ + +-spec init(rlx_state:t()) -> {ok, rlx_state:t()}. +init(State) -> + State1 = rlx_state:add_provider(State, providers:create([{name, ?PROVIDER}, + {module, ?MODULE}, + {deps, ?DEPS}])), + {ok, State1}. + +-spec do(rlx_state:t()) -> {ok, rlx_state:t()} | relx:error(). +do(State0) -> + LibDirs = get_lib_dirs(State0), + AppMeta = rlx_state:available_apps(State0), + case rlx_rel_discovery:do(State0, LibDirs, AppMeta) of + {ok, Releases} -> + {ok, rlx_state:realized_releases(State0, lists:foldl(fun add/2, + ec_dictionary:new(ec_dict), + Releases))}; + Error -> + Error + end. + +-spec format_error(any()) -> iolist(). +format_error(_) -> + "". + +%%%=================================================================== +%%% Internal Functions +%%%=================================================================== +%% @doc only add the release if its not documented in the system +add(Rel, Dict) -> + RelName = rlx_release:name(Rel), + RelVsn = rlx_release:vsn(Rel), + ec_dictionary:add({RelName, RelVsn}, Rel, Dict). + +get_lib_dirs(State) -> + LibDirs0 = rlx_state:lib_dirs(State), + case rlx_state:get(State, default_libs, true) of + false -> + LibDirs0; + true -> + lists:flatten([LibDirs0, + add_release_output_dir(State)]) + end. + +add_release_output_dir(State) -> + case rlx_state:get(State, disable_discover_release_output, false) of + true -> + []; + false -> + Output = erlang:iolist_to_binary(rlx_state:base_output_dir(State)), + case ec_file:exists(erlang:binary_to_list(Output)) of + true -> + Output; + false -> + [] + end + end. diff --git a/src/rlx_prv_release.erl b/src/rlx_prv_release.erl index bd5acc4..bd58434 100644 --- a/src/rlx_prv_release.erl +++ b/src/rlx_prv_release.erl @@ -18,22 +18,18 @@ %%% @author Eric Merritt <[email protected]> %%% @copyright (C) 2012 Erlware, LLC. %%% -%%% @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 provider behaviour. -module(rlx_prv_release). -behaviour(provider). -export([init/1, do/1, - format_error/2]). + format_error/1]). -include("relx.hrl"). -define(PROVIDER, resolve_release). --define(DEPS, [discover]). +-define(DEPS, [app_discover]). %%============================================================================ %% API @@ -43,12 +39,7 @@ init(State) -> State1 = rlx_state:add_provider(State, providers:create([{name, ?PROVIDER}, {module, ?MODULE}, - {bare, false}, - {deps, ?DEPS}, - {example, ""}, - {short_desc, ""}, - {desc, ""}, - {opts, []}])), + {deps, ?DEPS}])), {ok, State1}. %% @doc recursively dig down into the library directories specified in the state @@ -58,26 +49,31 @@ do(State) -> DepGraph = create_dep_graph(State), find_default_release(State, DepGraph). --spec format_error(ErrorDetail::term(), rlx_state:t()) -> iolist(). -format_error(no_goals_specified, _) -> +-spec format_error(ErrorDetail::term()) -> iolist(). +format_error(no_goals_specified) -> "No goals specified for this release ~n"; -format_error({no_release_name, Vsn}, _) -> +format_error({release_erts_error, Dir}) -> + io_lib:format("Unable to find erts in ~s~n", [Dir]); +format_error({no_release_name, Vsn}) -> io_lib:format("A target release version was specified (~s) but no name", [Vsn]); -format_error({invalid_release_info, Info}, _) -> +format_error({invalid_release_info, Info}) -> io_lib:format("Target release information is in an invalid format ~p", [Info]); -format_error({multiple_release_names, RelA, RelB}, _) -> +format_error({multiple_release_names, RelA, RelB}) -> io_lib:format("No default release name was specified and there are multiple " - "releases in the config: ~s, ~s", - [RelA, RelB]); -format_error(no_releases_in_system, _) -> + "releases in the config: ~s, ~s", + [RelA, RelB]); +format_error(no_releases_in_system) -> "No releases have been specified in the system!"; -format_error({no_releases_for, RelName}, _) -> +format_error({no_releases_for, RelName}) -> io_lib:format("No releases exist in the system for ~s!", [RelName]); -format_error({release_not_found, {RelName, RelVsn}}, _) -> +format_error({release_not_found, {RelName, RelVsn}}) -> io_lib:format("No releases exist in the system for ~p:~s!", [RelName, RelVsn]); -format_error({failed_solve, Error}, _) -> +format_error({failed_solve, Error}) -> io_lib:format("Failed to solve release:\n ~s", - [rlx_depsolver:format_error({error, Error})]). + [rlx_depsolver:format_error({error, Error})]); +format_error({release_error, Error}) -> + io_lib:format("Failed to resolve release:\n ~p~n", [Error]). + %%%=================================================================== %%% Internal Functions @@ -92,28 +88,30 @@ create_dep_graph(State) -> Deps = rlx_app_info:active_deps(App) ++ rlx_app_info:library_deps(App), rlx_depsolver:add_package_version(Graph1, - AppName, - AppVsn, - Deps) + AppName, + AppVsn, + Deps) end, Graph0, Apps). -spec find_default_release(rlx_state:t(), rlx_depsolver:t()) -> {ok, rlx_state:t()} | relx:error(). find_default_release(State, DepGraph) -> - try rlx_state:default_configured_release(State) of - {undefined, undefined} -> - resolve_default_release(State, DepGraph); - {RelName, undefined} -> - resolve_default_version(State, DepGraph, RelName); - {undefined, Vsn} -> - ?RLX_ERROR({no_release_name, Vsn}); - {RelName, RelVsn} -> - solve_release(State, DepGraph, RelName, RelVsn); - undefined -> - ?RLX_ERROR(no_releases_in_system) + try + case rlx_state:default_configured_release(State) of + {undefined, undefined} -> + resolve_default_release(State, DepGraph); + {RelName, undefined} -> + resolve_default_version(State, DepGraph, RelName); + {undefined, Vsn} -> + ?RLX_ERROR({no_release_name, Vsn}); + {RelName, RelVsn} -> + solve_release(State, DepGraph, RelName, RelVsn); + undefined -> + ?RLX_ERROR(no_releases_in_system) + end catch - {multiple_release_names, _, _}=Error -> + throw:{multiple_release_names, _, _}=Error -> ?RLX_ERROR(Error) end. @@ -156,8 +154,8 @@ release_sort({{RelA, _}, _}, {{RelB, _}, _}) -> solve_release(State0, DepGraph, RelName, RelVsn) -> ec_cmd_log:debug(rlx_state:log(State0), - "Solving Release ~p-~s~n", - [RelName, RelVsn]), + "Solving Release ~p-~s~n", + [RelName, RelVsn]), try Release = case get_realized_release(State0, RelName, RelVsn) of @@ -184,20 +182,32 @@ solve_release(State0, DepGraph, RelName, RelVsn) -> end. set_resolved(State, Release0, Pkgs) -> - case rlx_release:realize(Release0, Pkgs, rlx_state:available_apps(State)) of - {ok, Release1} -> - ec_cmd_log:info(rlx_state:log(State), - "Resolved ~p-~s~n", - [rlx_release:name(Release1), - rlx_release:vsn(Release1)]), - ec_cmd_log:debug(rlx_state:log(State), - fun() -> - rlx_release:format(0, Release1) - end), - {ok, rlx_state:add_realized_release(State, Release1)}; - {error, E} -> - ?RLX_ERROR({release_error, E}) - end. + case rlx_release:realize(Release0, Pkgs, rlx_state:available_apps(State)) of + {ok, Release1} -> + ec_cmd_log:info(rlx_state:log(State), + "Resolved ~p-~s~n", + [rlx_release:name(Release1), + rlx_release:vsn(Release1)]), + ec_cmd_log:debug(rlx_state:log(State), + fun() -> + rlx_release:format(0, Release1) + end), + case rlx_state:get(State, include_erts, undefined) of + IncludeErts when is_atom(IncludeErts) -> + {ok, rlx_state:add_realized_release(State, Release1)}; + ErtsDir -> + try + [Erts | _] = filelib:wildcard(filename:join(ErtsDir, "erts-*")), + [_, ErtsVsn] = string:tokens(filename:basename(Erts), "-"), + {ok, rlx_state:add_realized_release(State, rlx_release:erts(Release1, ErtsVsn))} + catch + _:_ -> + ?RLX_ERROR({release_erts_error, ErtsDir}) + end + end; + {error, E} -> + ?RLX_ERROR({release_error, E}) + end. get_realized_release(State, RelName, RelVsn) -> try diff --git a/src/rlx_prv_relup.erl b/src/rlx_prv_relup.erl index df6f831..6bf5d56 100644 --- a/src/rlx_prv_relup.erl +++ b/src/rlx_prv_relup.erl @@ -26,12 +26,12 @@ -export([init/1, do/1, - format_error/2]). + format_error/1]). -include("relx.hrl"). -define(PROVIDER, relup). --define(DEPS, [release]). +-define(DEPS, [rel_discover, release]). %%============================================================================ %% API @@ -41,12 +41,7 @@ init(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, []}])), + {deps, ?DEPS}])), {ok, State1}. -spec do(rlx_state:t()) -> {ok, rlx_state:t()} | relx:error(). @@ -55,24 +50,24 @@ do(State) -> Release0 = rlx_state:get_realized_release(State, RelName, RelVsn), make_relup(State, Release0). -format_error({relup_generation_error, CurrentName, UpFromName}, _) -> +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}, _) -> +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}, _) -> +format_error({no_upfrom_release_found, undefined}) -> io_lib:format("No earlier release for relup found", []); -format_error({no_upfrom_release_found, Vsn}, _) -> +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, _}}}, _) -> + {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}, State) -> +format_error({relup_script_generation_error, Module, Errors}) -> ["Errors generating relup \n", - rlx_util:indent(2), Module:format_error(Errors, State)]. + rlx_util:indent(2), Module:format_error(Errors)]. make_relup(State, Release) -> Vsn = rlx_state:upfrom(State), diff --git a/src/rlx_rel_discovery.erl b/src/rlx_rel_discovery.erl index b7c15bc..1cfdc87 100644 --- a/src/rlx_rel_discovery.erl +++ b/src/rlx_rel_discovery.erl @@ -18,10 +18,6 @@ %%% @author Eric Merritt <[email protected]> %%% @copyright (C) 2012 Erlware, LLC. %%% -%%% @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 provider behaviour. -module(rlx_rel_discovery). -export([do/3, diff --git a/src/rlx_release.erl b/src/rlx_release.erl index 7b82119..1d333bf 100644 --- a/src/rlx_release.erl +++ b/src/rlx_release.erl @@ -41,7 +41,7 @@ canonical_name/1, format/1, format/2, - format_error/2]). + format_error/1]). -export_type([t/0, name/0, @@ -217,14 +217,14 @@ format_goal({Constraint, AppType, AppInc}) -> format_goal(Constraint) -> rlx_depsolver:format_constraint(Constraint). --spec format_error(Reason::term(), rlx_state:t()) -> iolist(). -format_error({topo_error, E}, State) -> - rlx_topo:format_error(E, State); -format_error({failed_to_parse, Con}, _) -> +-spec format_error(Reason::term()) -> iolist(). +format_error({topo_error, E}) -> + rlx_topo:format_error(E); +format_error({failed_to_parse, Con}) -> io_lib:format("Failed to parse constraint ~p", [Con]); -format_error({invalid_constraint, _, Con}, _) -> +format_error({invalid_constraint, _, Con}) -> io_lib:format("Invalid constraint specified ~p", [Con]); -format_error({not_realized, Name, Vsn}, _) -> +format_error({not_realized, Name, Vsn}) -> io_lib:format("Unable to produce metadata release: ~p-~s has not been realized", [Name, Vsn]). diff --git a/src/rlx_state.erl b/src/rlx_state.erl index 550a44a..3bd818a 100644 --- a/src/rlx_state.erl +++ b/src/rlx_state.erl @@ -36,6 +36,10 @@ overrides/2, skip_apps/1, skip_apps/2, + exclude_apps/1, + exclude_apps/2, + debug_info/1, + debug_info/2, goals/1, goals/2, config_file/1, @@ -102,6 +106,8 @@ sys_config :: file:filename() | undefined, overrides=[] :: [{AppName::atom(), Directory::file:filename()}], skip_apps=[] :: [AppName::atom()], + exclude_apps=[] :: [AppName::atom()], + debug_info=keep :: keep | strip, configured_releases :: releases(), realized_releases :: releases(), dev_mode=false :: boolean(), @@ -154,7 +160,7 @@ new(Config, CommandLineConfig, Targets) 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), + State2 = rlx_state:put(State1, system_libs, true), State3 = rlx_state:put(State2, overlay_vars, []), create_logic_providers(State3). @@ -183,6 +189,23 @@ skip_apps(#state_t{skip_apps=Apps}) -> skip_apps(State, SkipApps) -> State#state_t{skip_apps=SkipApps}. +-spec exclude_apps(t()) -> [AppName::atom()]. +exclude_apps(#state_t{exclude_apps=Apps}) -> + Apps. + +%% @doc the application overrides for the system +-spec exclude_apps(t(), [AppName::atom()]) -> t(). +exclude_apps(State, SkipApps) -> + State#state_t{exclude_apps=SkipApps}. + +-spec debug_info(t()) -> keep | strip. +debug_info(#state_t{debug_info=DebugInfo}) -> + DebugInfo. + +-spec debug_info(t(), keep | strip) -> t(). +debug_info(State, DebugInfo) -> + State#state_t{debug_info=DebugInfo}. + %% @doc get the current log state for the system -spec log(t()) -> ec_cmd_log:t(). log(#state_t{log=LogState}) -> @@ -438,7 +461,8 @@ add_hook(post, {PreHooks, PostHooks}, Hook) -> -spec create_logic_providers(t()) -> t() | relx:error(). create_logic_providers(State) -> - create_all(State, [rlx_prv_discover, + create_all(State, [rlx_prv_app_discover, + rlx_prv_rel_discover, rlx_prv_overlay, rlx_prv_release, rlx_prv_assembler, diff --git a/src/rlx_topo.erl b/src/rlx_topo.erl index 1d5de7e..d24f227 100644 --- a/src/rlx_topo.erl +++ b/src/rlx_topo.erl @@ -34,7 +34,7 @@ -export([sort/1, sort_apps/1, - format_error/2]). + format_error/1]). -include("relx.hrl"). @@ -72,8 +72,8 @@ sort(Pairs) -> iterate(Pairs, [], all(Pairs)). %% @doc nicely format the error from the sort. --spec format_error(Reason::term(), rlx_state:t()) -> iolist(). -format_error({cycle, Pairs}, _) -> +-spec format_error(Reason::term()) -> iolist(). +format_error({cycle, Pairs}) -> ["Cycle detected in dependency graph, this must be resolved " "before we can continue:\n", case Pairs of |