aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorEric <[email protected]>2012-12-07 09:09:17 -0500
committerEric <[email protected]>2012-12-07 09:09:17 -0500
commitaac84f08f2b557ae2a7bf45001c47aec503203ab (patch)
treeb5e1d71e7935e0f1f3a9118638e05c574f8a63aa /src
parent89e5d70f95f4d23811500d6bf2643714f6c95fa2 (diff)
parent819690cd8bda0f7f91740b8fa5df71256656de52 (diff)
downloadrelx-aac84f08f2b557ae2a7bf45001c47aec503203ab.tar.gz
relx-aac84f08f2b557ae2a7bf45001c47aec503203ab.tar.bz2
relx-aac84f08f2b557ae2a7bf45001c47aec503203ab.zip
Merge remote-tracking branch 'canonical/next'
* canonical/next: fix nasty bug that didn't let relcool output errors make the default output _rel instead of relcool_output allow a user to specify additional opts to erlexec add examples of simple and complete relcool configs make sure ebin is removed on clean add inadvertantly deleted relcool.app.src support travis CI in relcool cleanup the rebar config remove docs (they have been moved to the wiki) all relcool to symlink in 'overridden' apps rcl_prv_discover now supports setting up 'link' type app_info messages support specifing overrides in the configuration support a new 'link' field in rcl_app_info minor cleanup and refactoring for rcl_prv_assembler, rcl_prv_discover
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.