diff options
author | Eric <[email protected]> | 2012-12-07 09:09:17 -0500 |
---|---|---|
committer | Eric <[email protected]> | 2012-12-07 09:09:17 -0500 |
commit | aac84f08f2b557ae2a7bf45001c47aec503203ab (patch) | |
tree | b5e1d71e7935e0f1f3a9118638e05c574f8a63aa /src | |
parent | 89e5d70f95f4d23811500d6bf2643714f6c95fa2 (diff) | |
parent | 819690cd8bda0f7f91740b8fa5df71256656de52 (diff) | |
download | relx-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.erl | 26 | ||||
-rw-r--r-- | src/rcl_cmd_args.erl | 2 | ||||
-rw-r--r-- | src/rcl_provider.erl | 7 | ||||
-rw-r--r-- | src/rcl_prv_assembler.erl | 37 | ||||
-rw-r--r-- | src/rcl_prv_config.erl | 3 | ||||
-rw-r--r-- | src/rcl_prv_discover.erl | 53 | ||||
-rw-r--r-- | src/rcl_state.erl | 14 | ||||
-rw-r--r-- | src/relcool.erl | 34 |
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. |