Skip to content

Commit

Permalink
add a dev mode that symlinks the release instead of copying it
Browse files Browse the repository at this point in the history
This should only ever be used for development, however it makes it
very, very nice to be able to simple recompile a project without
recopying it to try new things.
  • Loading branch information
ericbmerritt committed Oct 15, 2013
1 parent a54bb2b commit 51b7508
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 10 deletions.
2 changes: 2 additions & 0 deletions src/relx.erl
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,8 @@ opt_spec_list() ->
"Whether to use the default system added lib dirs (means you must add them all manually). Default is true"},
{log_level, $V, "verbose", {integer, 2},
"Verbosity level, maybe between 0 and 3"},
{dev_mode, $d, "dev-mode", {boolean, false},
"Symlink the applications and configuration into the release instead of copying"},
{override_app, $a, "override_app", string,
"Provide an app name and a directory to override in the form <appname>:<app directory>"},
{config, $c, "config", {string, ""}, "The path to a config file"},
Expand Down
45 changes: 39 additions & 6 deletions src/rlx_prv_assembler.erl
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ init(State) ->
%% looking for OTP Applications
-spec do(rlx_state:t()) -> {ok, rlx_state:t()} | relx:error().
do(State) ->
print_dev_mode(State),
{RelName, RelVsn} = rlx_state:default_configured_release(State),
Release = rlx_state:get_realized_release(State, RelName, RelVsn),
OutputDir = rlx_state:output_dir(State),
Expand Down Expand Up @@ -139,6 +140,15 @@ format_error({tar_generation_error, Module, Errors}) ->
%%%===================================================================
%%% Internal Functions
%%%===================================================================
print_dev_mode(State) ->
case rlx_state:dev_mode(State) of
true ->
ec_cmd_log:info(rlx_state:log(State),
"Dev mode enabled, release will be symlinked");
false ->
ok
end.

-spec create_output_dir(file:name()) ->
ok | {error, Reason::term()}.
create_output_dir(OutputDir) ->
Expand All @@ -157,7 +167,7 @@ create_output_dir(OutputDir) ->
copy_app_directories_to_output(State, Release, OutputDir) ->
LibDir = filename:join([OutputDir, "lib"]),
ok = ec_file:mkdir_p(LibDir),
Apps = rlx_release:application_details(Release),
Apps = prepare_applications(State, rlx_release:application_details(Release)),
Result = lists:filter(fun({error, _}) ->
true;
(_) ->
Expand All @@ -173,6 +183,14 @@ copy_app_directories_to_output(State, Release, OutputDir) ->
create_release_info(State, Release, OutputDir)
end.

prepare_applications(State, Apps) ->
case rlx_state:dev_mode(State) of
true ->
[rlx_app_info:link(App, true) || App <- Apps];
false ->
Apps
end.

copy_app(LibDir, App) ->
AppName = erlang:atom_to_list(rlx_app_info:name(App)),
AppVsn = rlx_app_info:vsn_as_string(App),
Expand Down Expand Up @@ -323,13 +341,29 @@ copy_or_generate_sys_config_file(State, Release, OutputDir, RelDir) ->
false ->
?RLX_ERROR({config_does_not_exist, ConfigPath});
true ->
ok = ec_file:copy(ConfigPath, RelSysConfPath),
include_erts(State, Release, OutputDir, RelDir)
copy_or_symlink_sys_config_file(State, Release, OutputDir, RelDir,
ConfigPath, RelSysConfPath)
end
end.

%% @doc copy config/sys.config or generate one to releases/VSN/sys.config
-spec copy_or_symlink_sys_config_file(rlx_state:t(), rlx_release:t(),
file:name(), file:name(),
file:name(), file:name()) ->
{ok, rlx_state:t()} | relx:error().
copy_or_symlink_sys_config_file(State, Release, OutputDir, RelDir,
ConfigPath, RelSysConfPath) ->
case rlx_state:dev_mode(State) of
true ->
ok = file:make_symlink(ConfigPath, RelSysConfPath);
_ ->
ok = ec_file:copy(ConfigPath, RelSysConfPath)
end,
include_erts(State, Release, OutputDir, RelDir).

%% @doc Optionally add erts directory to release, if defined.
-spec include_erts(rlx_state:t(), rlx_release:t(), file:name(), file:name()) -> {ok, rlx_state:t()} | relx:error().
-spec include_erts(rlx_state:t(), rlx_release:t(), file:name(), file:name()) ->
{ok, rlx_state:t()} | relx:error().
include_erts(State, Release, OutputDir, RelDir) ->
case rlx_state:get(State, include_erts, true) of
true ->
Expand Down Expand Up @@ -383,7 +417,7 @@ make_boot_script(State, Release, OutputDir, RelDir) ->
end) of
ok ->
ec_cmd_log:error(rlx_state:log(State),
"release successfully created!"),
"release successfully created!"),
create_RELEASES(OutputDir, ReleaseFile),
{ok, State};
error ->
Expand Down Expand Up @@ -1171,7 +1205,6 @@ append_node_suffix(Name, Suffix) ->
list_to_atom(lists:concat([Node, Suffix, os:getpid()]))
end.
%%
%% Given a string or binary, parse it into a list of terms, ala file:consult/0
%%
Expand Down
3 changes: 2 additions & 1 deletion src/rlx_prv_config.erl
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@ parent_dir([_H], Acc) ->
parent_dir([H | T], Acc) ->
parent_dir(T, [H | Acc]).


-spec load_config(file:filename(), rlx_state:t()) ->
{ok, rlx_state:t()} | relx:error().
load_config(ConfigFile, State) ->
Expand Down Expand Up @@ -153,6 +152,8 @@ load_terms({skip_apps, SkipApps0}, {ok, State0}) ->
{ok, rlx_state:skip_apps(State0, SkipApps0)};
load_terms({overrides, Overrides0}, {ok, State0}) ->
{ok, rlx_state:overrides(State0, Overrides0)};
load_terms({dev_mode, DevMode}, {ok, State0}) ->
{ok, rlx_state:dev_mode(State0, DevMode)};
load_terms({release, {RelName, Vsn}, Applications}, {ok, State0}) ->
Release0 = rlx_release:new(RelName, Vsn),
case rlx_release:goals(Release0, Applications) of
Expand Down
14 changes: 13 additions & 1 deletion src/rlx_state.erl
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
put/3,
caller/1,
caller/2,
dev_mode/1,
dev_mode/2,
upfrom/1,
format/1,
format/2]).
Expand Down Expand Up @@ -88,6 +90,7 @@
skip_apps=[] :: [AppName::atom()],
configured_releases :: releases(),
realized_releases :: releases(),
dev_mode=false :: boolean(),
upfrom :: string() | binary() | undefined,
config_values :: ec_dictionary:dictionary(Key::atom(),
Value::term())}).
Expand Down Expand Up @@ -209,7 +212,7 @@ vm_args(#state_t{vm_args=VmArgs}) ->

-spec vm_args(t(), file:filename()) -> t().
vm_args(State, VmArgs) ->
State#state_t{vm_args=VmArgs}.
State#state_t{vm_args=VmArgs}.

-spec sys_config(t()) -> file:filename() | undefined.
sys_config(#state_t{sys_config=SysConfig}) ->
Expand Down Expand Up @@ -317,6 +320,15 @@ caller(#state_t{caller=Caller}) ->
caller(S, Caller) ->
S#state_t{caller=Caller}.

-spec dev_mode(t()) -> boolean().
dev_mode(#state_t{dev_mode=DevMode}) ->
DevMode.

-spec dev_mode(t(), boolean()) -> t().
dev_mode(S, DevMode) ->
S#state_t{dev_mode=DevMode}.


-spec upfrom(t()) -> string() | binary() | undefined.
upfrom(#state_t{upfrom=UpFrom}) ->
UpFrom.
Expand Down
46 changes: 44 additions & 2 deletions test/rlx_release_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@
make_invalid_config_release/1,
make_relup_release/1,
make_relup_release2/1,
make_one_app_top_level_release/1]).
make_one_app_top_level_release/1,
make_dev_mode_release/1]).

-include_lib("common_test/include/ct.hrl").
-include_lib("eunit/include/eunit.hrl").
Expand Down Expand Up @@ -69,7 +70,7 @@ all() ->
make_implicit_config_release, make_rerun_overridden_release,
overlay_release, make_goalless_release, make_depfree_release,
make_invalid_config_release, make_relup_release, make_relup_release2,
make_one_app_top_level_release].
make_one_app_top_level_release, make_dev_mode_release].

make_release(Config) ->
LibDir1 = proplists:get_value(lib1, Config),
Expand Down Expand Up @@ -832,6 +833,46 @@ make_one_app_top_level_release(Config) ->
?assert(lists:keymember(kernel, 1, AppSpecs)),
?assert(lists:member({goal_app_1, "0.0.1"}, AppSpecs)).

make_dev_mode_release(Config) ->
LibDir1 = proplists:get_value(lib1, Config),
[(fun({Name, Vsn}) ->
create_app(LibDir1, Name, Vsn, [kernel, stdlib], [])
end)(App)
||
App <-
[{create_random_name("lib_app1_"), create_random_vsn()}
|| _ <- lists:seq(1, 100)]],

create_app(LibDir1, "goal_app_1", "0.0.1", [stdlib,kernel,non_goal_1], []),
create_app(LibDir1, "lib_dep_1", "0.0.1", [stdlib,kernel], []),
create_app(LibDir1, "goal_app_2", "0.0.1", [stdlib,kernel,goal_app_1,non_goal_2], []),
create_app(LibDir1, "non_goal_1", "0.0.1", [stdlib,kernel], [lib_dep_1]),
create_app(LibDir1, "non_goal_2", "0.0.1", [stdlib,kernel], []),

SysConfig = filename:join([LibDir1, "config", "sys.config"]),
write_config(SysConfig, [{this_is_a_test, "yup it is"}]),

ConfigFile = filename:join([LibDir1, "relx.config"]),
write_config(ConfigFile,
[{dev_mode, true},
{sys_config, SysConfig},
{release, {foo, "0.0.1"},
[goal_app_1,
goal_app_2]}]),
OutputDir = filename:join([proplists:get_value(data_dir, Config),
create_random_name("relx-output")]),
{ok, State} = relx:do(undefined, undefined, [], [LibDir1], 3,
OutputDir, ConfigFile),
[{{foo, "0.0.1"}, _Release}] = ec_dictionary:to_list(rlx_state:realized_releases(State)),

?assert(ec_file:is_symlink(filename:join([OutputDir, "lib", "non_goal_1-0.0.1"]))),
?assert(ec_file:is_symlink(filename:join([OutputDir, "lib", "non_goal_2-0.0.1"]))),
?assert(ec_file:is_symlink(filename:join([OutputDir, "lib", "goal_app_1-0.0.1"]))),
?assert(ec_file:is_symlink(filename:join([OutputDir, "lib", "goal_app_2-0.0.1"]))),
?assert(ec_file:is_symlink(filename:join([OutputDir, "lib", "lib_dep_1-0.0.1"]))),
?assert(ec_file:is_symlink(filename:join([OutputDir, "releases", "0.0.1",
"sys.config"]))).


%%%===================================================================
%%% Helper Functions
Expand Down Expand Up @@ -888,6 +929,7 @@ create_random_vsn() ->
".", erlang:integer_to_list(random:uniform(100))]).

write_config(Filename, Values) ->
ok = filelib:ensure_dir(Filename),
ok = ec_file:write(Filename,
[io_lib:format("~p.\n", [Val]) || Val <- Values]).

Expand Down

0 comments on commit 51b7508

Please sign in to comment.