aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/relx.erl27
-rw-r--r--src/rlx_app_discovery.erl49
-rw-r--r--src/rlx_cmd_args.erl38
-rw-r--r--src/rlx_config.erl64
-rw-r--r--src/rlx_prv_app_discover.erl (renamed from src/rlx_prv_discover.erl)67
-rw-r--r--src/rlx_prv_archive.erl56
-rw-r--r--src/rlx_prv_assembler.erl121
-rw-r--r--src/rlx_prv_overlay.erl118
-rw-r--r--src/rlx_prv_rel_discover.erl92
-rw-r--r--src/rlx_prv_release.erl120
-rw-r--r--src/rlx_prv_relup.erl25
-rw-r--r--src/rlx_rel_discovery.erl4
-rw-r--r--src/rlx_release.erl14
-rw-r--r--src/rlx_state.erl28
-rw-r--r--src/rlx_topo.erl6
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