aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/rcl_app_info.erl26
-rw-r--r--src/rcl_cmd_args.erl2
-rw-r--r--src/rcl_provider.erl7
-rw-r--r--src/rcl_prv_assembler.erl37
-rw-r--r--src/rcl_prv_config.erl3
-rw-r--r--src/rcl_prv_discover.erl53
-rw-r--r--src/rcl_state.erl14
-rw-r--r--src/relcool.erl34
8 files changed, 134 insertions, 42 deletions
diff --git a/src/rcl_app_info.erl b/src/rcl_app_info.erl
index d0192e6..bc64e30 100644
--- a/src/rcl_app_info.erl
+++ b/src/rcl_app_info.erl
@@ -49,6 +49,8 @@
active_deps/2,
library_deps/1,
library_deps/2,
+ link/1,
+ link/2,
format_error/1,
format/2,
format/1]).
@@ -60,6 +62,7 @@
-record(app_info_t, {name :: atom(),
vsn :: ec_semver:semver(),
dir :: file:name(),
+ link=false :: boolean(),
active_deps :: [atom()],
library_deps :: [atom()]}).
@@ -80,7 +83,13 @@ new() ->
%% @doc build a complete version of the app info with all fields set.
-spec new(atom(), string(), file:name(), [atom()], [atom()]) ->
{ok, t()} | relcool:error().
-new(AppName, Vsn, Dir, ActiveDeps, LibraryDeps)
+new(AppName, Vsn, Dir, ActiveDeps, LibraryDeps) ->
+ new(AppName, Vsn, Dir, ActiveDeps, LibraryDeps, false).
+
+%% @doc build a complete version of the app info with all fields set.
+-spec new(atom(), string(), file:name(), [atom()], [atom()], boolean()) ->
+ {ok, t()} | relcool:error().
+new(AppName, Vsn, Dir, ActiveDeps, LibraryDeps, Link)
when erlang:is_atom(AppName),
erlang:is_list(Dir),
erlang:is_list(ActiveDeps),
@@ -91,7 +100,8 @@ new(AppName, Vsn, Dir, ActiveDeps, LibraryDeps)
ParsedVsn ->
{ok, #app_info_t{name=AppName, vsn=ParsedVsn, dir=Dir,
active_deps=ActiveDeps,
- library_deps=LibraryDeps}}
+ library_deps=LibraryDeps,
+ link=Link}}
end.
-spec name(t()) -> atom().
@@ -145,6 +155,14 @@ library_deps(AppInfo=#app_info_t{}, LibraryDeps)
when erlang:is_list(LibraryDeps) ->
AppInfo#app_info_t{library_deps=LibraryDeps}.
+-spec link(t()) -> boolean().
+link(#app_info_t{link=Link}) ->
+ Link.
+
+-spec link(t(), boolean()) -> t().
+link(AppInfo, NewLink) ->
+ AppInfo#app_info_t{link=NewLink}.
+
-spec format_error(Reason::term()) -> iolist().
format_error({vsn_parse, AppName}) ->
io_lib:format("Error parsing version for ~p",
@@ -156,9 +174,11 @@ format(AppInfo) ->
-spec format(non_neg_integer(), t()) -> iolist().
format(Indent, #app_info_t{name=Name, vsn=Vsn, dir=Dir,
- active_deps=Deps, library_deps=LibDeps}) ->
+ active_deps=Deps, library_deps=LibDeps,
+ link=Link}) ->
[rcl_util:indent(Indent), erlang:atom_to_list(Name), "-", ec_semver:format(Vsn),
": ", Dir, "\n",
+ rcl_util:indent(Indent + 1), "Symlink: ", erlang:atom_to_list(Link), "\n",
rcl_util:indent(Indent + 1), "Active Dependencies:\n",
[[rcl_util:indent(Indent + 2), erlang:atom_to_list(Dep), ",\n"] || Dep <- Deps],
rcl_util:indent(Indent + 1), "Library Dependencies:\n",
diff --git a/src/rcl_cmd_args.erl b/src/rcl_cmd_args.erl
index ab073a2..68bd9ce 100644
--- a/src/rcl_cmd_args.erl
+++ b/src/rcl_cmd_args.erl
@@ -147,7 +147,7 @@ convert_goals([RawSpec | Rest], Acc) ->
-spec create_output_dir([getopt:option()], rcl_state:cmd_args()) ->
{ok, rcl_state:cmd_args()} | relcool:error().
create_output_dir(Opts, Acc) ->
- OutputDir = proplists:get_value(output_dir, Opts, "./relcool_output"),
+ OutputDir = proplists:get_value(output_dir, Opts, "./_rel"),
case filelib:is_dir(OutputDir) of
false ->
case rcl_util:mkdir_p(OutputDir) of
diff --git a/src/rcl_provider.erl b/src/rcl_provider.erl
index 9d1dd88..c3ef434 100644
--- a/src/rcl_provider.erl
+++ b/src/rcl_provider.erl
@@ -10,6 +10,7 @@
%% API
-export([new/2,
do/2,
+ impl/1,
format_error/1,
format_error/2,
format/1]).
@@ -57,6 +58,12 @@ new(ModuleName, State0) when is_atom(ModuleName) ->
do({?MODULE, Mod}, State) ->
Mod:do(State).
+%%% @doc get the name of the module that implements the provider
+%%% @param Provider the provider object
+-spec impl(Provider::t()) -> module().
+impl({?MODULE, Mod}) ->
+ Mod.
+
%% @doc format an error produced from a provider.
-spec format_error(Reason::term()) -> iolist().
format_error({non_existing, ModuleName}) ->
diff --git a/src/rcl_prv_assembler.erl b/src/rcl_prv_assembler.erl
index a118b12..c0f65c9 100644
--- a/src/rcl_prv_assembler.erl
+++ b/src/rcl_prv_assembler.erl
@@ -73,8 +73,6 @@ format_error({release_script_generation_error, Module, Errors}) ->
["Errors generating release \n",
rcl_util:indent(1), Module:format_error(Errors)].
-
-
%%%===================================================================
%%% Internal Functions
%%%===================================================================
@@ -102,15 +100,20 @@ copy_app(LibDir, App) ->
AppVsn = rcl_app_info:vsn_as_string(App),
AppDir = rcl_app_info:dir(App),
TargetDir = filename:join([LibDir, AppName ++ "-" ++ AppVsn]),
- ec_plists:map(fun(SubDir) ->
- copy_dir(AppDir, TargetDir, SubDir)
- end, ["ebin",
- "include",
- "priv",
- "src",
- "c_src",
- "README",
- "LICENSE"]).
+ case rcl_app_info:link(App) of
+ true ->
+ file:make_symlink(AppDir, TargetDir);
+ false ->
+ ec_plists:map(fun(SubDir) ->
+ copy_dir(AppDir, TargetDir, SubDir)
+ end, ["ebin",
+ "include",
+ "priv",
+ "src",
+ "c_src",
+ "README",
+ "LICENSE"])
+ end.
copy_dir(AppDir, TargetDir, SubDir) ->
SubSource = filename:join(AppDir, SubDir),
@@ -157,7 +160,10 @@ write_bin_file(State, Release, OutputDir, RelDir) ->
ok = ec_file:mkdir_p(BinDir),
VsnRel = filename:join(BinDir, RelName ++ "-" ++ RelVsn),
BareRel = filename:join(BinDir, RelName),
- StartFile = bin_file_contents(RelName, RelVsn, rcl_release:erts(Release)),
+ ErlOpts = rcl_state:get(State, erl_opts, ""),
+ StartFile = bin_file_contents(RelName, RelVsn,
+ rcl_release:erts(Release),
+ ErlOpts),
ok = file:write_file(VsnRel, StartFile),
ok = file:change_mode(VsnRel, 8#777),
ok = file:write_file(BareRel, StartFile),
@@ -275,7 +281,7 @@ get_code_paths(Release, OutDir) ->
rcl_app_info:vsn_as_string(App), "ebin"]) ||
App <- rcl_release:application_details(Release)].
-bin_file_contents(RelName, RelVsn, ErtsVsn) ->
+bin_file_contents(RelName, RelVsn, ErtsVsn, ErlOpts) ->
[<<"#!/bin/sh
set -e
@@ -286,6 +292,7 @@ REL_NAME=">>, RelName, <<"
REL_VSN=">>, RelVsn, <<"
ERTS_VSN=">>, ErtsVsn, <<"
REL_DIR=$RELEASE_ROOT_DIR/releases/$REL_NAME-$REL_VSN
+ERL_OPTS=">>, ErlOpts, <<"
ERTS_DIR=
SYS_CONFIG=
@@ -324,6 +331,4 @@ export EMU=beam
export PROGNAME=erl
export LD_LIBRARY_PATH=$ERTS_DIR/lib
-
-
-$BINDIR/erlexec $SYS_CONFIG -boot $REL_DIR/$REL_NAME $@">>].
+$BINDIR/erlexec $ERL_OPTS $SYS_CONFIG -boot $REL_DIR/$REL_NAME $@">>].
diff --git a/src/rcl_prv_config.erl b/src/rcl_prv_config.erl
index 7a1cd19..ee1c770 100644
--- a/src/rcl_prv_config.erl
+++ b/src/rcl_prv_config.erl
@@ -82,7 +82,8 @@ load_terms({add_providers, Providers0}, {ok, State0}) ->
ExistingProviders = rcl_state:providers(State1),
{ok, rcl_state:providers(State1, ExistingProviders ++ Providers3)}
end;
-
+load_terms({overrides, Overrides0}, {ok, State0}) ->
+ {ok, rcl_state:overrides(State0, Overrides0)};
load_terms({release, {RelName, Vsn}, Applications}, {ok, State0}) ->
Release0 = rcl_release:new(RelName, Vsn),
case rcl_release:goals(Release0, Applications) of
diff --git a/src/rcl_prv_discover.erl b/src/rcl_prv_discover.erl
index 23a3937..0a12d24 100644
--- a/src/rcl_prv_discover.erl
+++ b/src/rcl_prv_discover.erl
@@ -49,16 +49,28 @@ do(State) ->
["Resolving OTP Applications from directories:\n",
[[rcl_util:indent(1), LibDir, "\n"] || LibDir <- LibDirs]]
end),
+ resolve_app_metadata(State, LibDirs, OutputDir).
+-spec format_error([ErrorDetail::term()]) -> iolist().
+format_error(ErrorDetails)
+ when erlang:is_list(ErrorDetails) ->
+ [[format_detail(ErrorDetail), "\n"] || ErrorDetail <- ErrorDetails].
+
+%%%===================================================================
+%%% Internal Functions
+%%%===================================================================
+resolve_app_metadata(State, LibDirs, OutputDir) ->
AppMeta0 = lists:flatten(ec_plists:map(fun(LibDir) ->
discover_dir([OutputDir],
LibDir)
end, LibDirs)),
+ AppMeta1 = setup_overrides(State, AppMeta0),
+
Errors = [case El of
{error, Ret} -> Ret;
_ -> El
end
- || El <- AppMeta0,
+ || El <- AppMeta1,
case El of
{error, _} ->
true;
@@ -66,30 +78,38 @@ do(State) ->
false
end],
- lists:filter(fun({error, _}) -> true;
- (_) -> false
- end, AppMeta0),
case Errors of
[] ->
- AppMeta1 = lists:flatten(AppMeta0),
+ AppMeta2 = lists:flatten(AppMeta1),
rcl_log:debug(rcl_state:log(State),
fun() ->
["Resolved the following OTP Applications from the system: \n",
- [[rcl_app_info:format(1, App), "\n"] || App <- AppMeta1]]
+ [[rcl_app_info:format(1, App), "\n"] || App <- AppMeta2]]
end),
- {ok, rcl_state:available_apps(State, AppMeta1)};
+ {ok, rcl_state:available_apps(State, AppMeta2)};
_ ->
?RCL_ERROR(Errors)
end.
--spec format_error([ErrorDetail::term()]) -> iolist().
-format_error(ErrorDetails)
- when erlang:is_list(ErrorDetails) ->
- [[format_detail(ErrorDetail), "\n"] || ErrorDetail <- ErrorDetails].
+app_name({error, _}) ->
+ undefined;
+app_name(AppMeta) ->
+ rcl_app_info:name(AppMeta).
+
+setup_overrides(State, AppMetas0) ->
+ Overrides = rcl_state:overrides(State),
+ AppMetas1 = [AppMeta || AppMeta <- AppMetas0,
+ not lists:keymember(app_name(AppMeta), 1, Overrides)],
+ [case is_valid_otp_app(filename:join([FileName, "ebin",
+ erlang:atom_to_list(AppName) ++ ".app"])) of
+ [] ->
+ {error, {invalid_override, AppName, FileName}};
+ Error = {error, _} ->
+ Error;
+ App ->
+ rcl_app_info:link(App, true)
+ end || {AppName, FileName} <- Overrides] ++ AppMetas1.
-%%%===================================================================
-%%% Internal Functions
-%%%===================================================================
get_lib_dirs(State) ->
LibDirs0 = rcl_state:lib_dirs(State),
add_rebar_deps_dir(State, LibDirs0).
@@ -133,6 +153,9 @@ add_system_lib_dir(State, LibDirs) ->
end.
-spec format_detail(ErrorDetail::term()) -> iolist().
+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}) ->
io_lib:format("permission denied accessing file ~s", [File]);
format_detail({accessing, File, Type}) ->
@@ -172,7 +195,7 @@ discover_dir(IgnoreDirs, File) ->
is_valid_otp_app(File)
end.
--spec is_valid_otp_app(file:name()) -> [rcl_app_info:t() | {error, Reason::term()} | []].
+-spec is_valid_otp_app(file:name()) -> rcl_app_info:t() | {error, Reason::term()} | [].
is_valid_otp_app(File) ->
%% Is this an ebin dir?
EbinDir = filename:dirname(File),
diff --git a/src/rcl_state.erl b/src/rcl_state.erl
index 46c91c7..7397e6f 100644
--- a/src/rcl_state.erl
+++ b/src/rcl_state.erl
@@ -27,6 +27,8 @@
log/1,
output_dir/1,
lib_dirs/1,
+ overrides/1,
+ overrides/2,
goals/1,
config_files/1,
providers/1,
@@ -64,6 +66,7 @@
available_apps = [] :: [rcl_app_info:t()],
default_release :: {rcl_release:name(), rcl_release:vsn()},
sys_config :: file:filename() | undefined,
+ overrides :: [{AppName::atom(), Directory::file:filename()}],
releases :: ec_dictionary:dictionary({ReleaseName::atom(),
ReleaseVsn::string()},
rcl_release:t()),
@@ -96,10 +99,21 @@ new(PropList, Targets) when erlang:is_list(PropList) ->
providers = [],
releases=ec_dictionary:new(ec_dict),
config_values=ec_dictionary:new(ec_dict),
+ overrides = proplists:get_value(overrides, PropList, []),
default_release={proplists:get_value(relname, PropList, undefined),
proplists:get_value(relvsn, PropList, undefined)}},
create_logic_providers(State0).
+%% @doc the application overrides for the system
+-spec overrides(t()) -> [{AppName::atom(), Directory::file:filename()}].
+overrides(#state_t{overrides=Overrides}) ->
+ Overrides.
+
+%% @doc the application overrides for the system
+-spec overrides(t(), [{AppName::atom(), Directory::file:filename()}]) -> t().
+overrides(State, Overrides) ->
+ State#state_t{overrides=Overrides}.
+
%% @doc get the current log state for the system
-spec log(t()) -> rcl_log:t().
log(#state_t{log=LogState}) ->
diff --git a/src/relcool.erl b/src/relcool.erl
index 44debc9..0d75c62 100644
--- a/src/relcool.erl
+++ b/src/relcool.erl
@@ -22,6 +22,7 @@
-export([main/1,
do/7,
+ do/8,
format_error/1,
opt_spec_list/0]).
@@ -59,15 +60,30 @@ main(Args) ->
%% @param OutputDir - The directory where the release should be built to
%% @param Configs - The list of config files for the system
do(RelName, RelVsn, Goals, LibDirs, LogLevel, OutputDir, Configs) ->
+ do(RelName, RelVsn, Goals, LibDirs, LogLevel, OutputDir, [], Configs).
+
+%% @doc provides an API to run the Relcool process from erlang applications
+%%
+%% @param RelName - The release name to build (maybe `undefined`)
+%% @param RelVsn - The release version to build (maybe `undefined`)
+%% @param Goals - The release goals for the system in depsolver or Relcool goal
+%% format
+%% @param LibDirs - The library dirs that should be used for the system
+%% @param OutputDir - The directory where the release should be built to
+%% @param Overrides - A list of overrides for the system
+%% @param Configs - The list of config files for the system
+do(RelName, RelVsn, Goals, LibDirs, LogLevel, OutputDir, Overrides, Configs) ->
State = rcl_state:new([{relname, RelName},
{relvsn, RelVsn},
{goals, Goals},
+ {overrides, Overrides},
{output_dir, OutputDir},
{lib_dirs, LibDirs},
{log, rcl_log:new(LogLevel)}],
Configs),
run_relcool_process(rcl_state:caller(State, api)).
+
-spec opt_spec_list() -> [getopt:option_spec()].
opt_spec_list() ->
[
@@ -112,14 +128,17 @@ run_providers(State0) ->
{ok, State1} ->
Providers = rcl_state:providers(State1),
Result = run_providers(ConfigProvider, Providers, State1),
- case rcl_state:caller(State1) of
- command_line ->
- init:stop(0);
- api ->
- Result
- end
+ handle_output(State1, rcl_state:caller(State1), Result)
end.
+handle_output(State, command_line, E={error, _}) ->
+ report_error(State, E),
+ init:stop(127);
+handle_output(_State, command_line, _) ->
+ init:stop(0);
+handle_output(_State, api, Result) ->
+ Result.
+
run_providers(ConfigProvider, Providers, State0) ->
case Providers of
[ConfigProvider | Rest] ->
@@ -135,10 +154,13 @@ run_providers(ConfigProvider, Providers, State0) ->
run_provider(_Provider, Error = {error, _}) ->
Error;
run_provider(Provider, {ok, State0}) ->
+ rcl_log:debug(rcl_state:log(State0), "Running provider ~p~n",
+ [rcl_provider:impl(Provider)]),
case rcl_provider:do(Provider, State0) of
{ok, State1} ->
{ok, State1};
E={error, _} ->
+
E
end.