aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuis Rascao <[email protected]>2016-11-20 22:19:08 +0000
committerLuis Rascao <[email protected]>2016-11-21 00:24:37 +0000
commit81369d99a9b6ee5caae6b5e6a5faffb8a65fb588 (patch)
tree8b3e2acce5896f977bd264fb1cf66a9ffe6e6fd6
parentc305c89713abf48570e2db152b367f00ac641893 (diff)
downloadrelx-81369d99a9b6ee5caae6b5e6a5faffb8a65fb588.tar.gz
relx-81369d99a9b6ee5caae6b5e6a5faffb8a65fb588.tar.bz2
relx-81369d99a9b6ee5caae6b5e6a5faffb8a65fb588.zip
Provide a new config directive that allows per-app module exclusion
By introducing a new entry in the config file allow excluding specific modules from a given app, they will not be copied onto the final release and their reference removed from the .app file. The new entry takes on the following form: {exclude_modules, [ {App :: atom(), [Module :: atom()]} ]}
-rw-r--r--src/rlx_config.erl2
-rw-r--r--src/rlx_prv_assembler.erl102
-rw-r--r--src/rlx_state.erl14
-rw-r--r--test/rlx_release_SUITE.erl45
-rw-r--r--test/rlx_test_utils.erl43
5 files changed, 156 insertions, 50 deletions
diff --git a/src/rlx_config.erl b/src/rlx_config.erl
index 1dbfa13..dfcb511 100644
--- a/src/rlx_config.erl
+++ b/src/rlx_config.erl
@@ -173,6 +173,8 @@ 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({exclude_modules, ExcludeModules0}, {ok, State0}) ->
+ {ok, rlx_state:exclude_modules(State0, ExcludeModules0)};
load_terms({debug_info, DebugInfo}, {ok, State0}) ->
{ok, rlx_state:debug_info(State0, DebugInfo)};
load_terms({overrides, Overrides0}, {ok, State0}) ->
diff --git a/src/rlx_prv_assembler.erl b/src/rlx_prv_assembler.erl
index 7ff1d61..3829c82 100644
--- a/src/rlx_prv_assembler.erl
+++ b/src/rlx_prv_assembler.erl
@@ -162,7 +162,7 @@ copy_app_directories_to_output(State, Release, OutputDir) ->
false
end,
lists:flatten(ec_plists:map(fun(App) ->
- copy_app(LibDir, App, IncludeSrc, IncludeErts)
+ copy_app(State, LibDir, App, IncludeSrc, IncludeErts)
end, Apps))),
case Result of
[E | _] ->
@@ -179,7 +179,7 @@ prepare_applications(State, Apps) ->
Apps
end.
-copy_app(LibDir, App, IncludeSrc, IncludeErts) ->
+copy_app(State, 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),
@@ -196,52 +196,67 @@ copy_app(LibDir, App, IncludeSrc, IncludeErts) ->
true ->
[];
false ->
- copy_app_(App, AppDir, TargetDir, IncludeSrc)
+ copy_app_(State, App, AppDir, TargetDir, IncludeSrc)
end;
_ ->
- copy_app_(App, AppDir, TargetDir, IncludeSrc)
+ copy_app_(State, App, AppDir, TargetDir, IncludeSrc)
end
end.
is_erts_lib(Dir) ->
lists:prefix(filename:split(list_to_binary(code:lib_dir())), filename:split(Dir)).
-copy_app_(App, AppDir, TargetDir, IncludeSrc) ->
+copy_app_(State, App, AppDir, TargetDir, IncludeSrc) ->
remove_symlink_or_directory(TargetDir),
case rlx_app_info:link(App) of
true ->
link_directory(AppDir, TargetDir),
- rewrite_app_file(App, AppDir);
+ rewrite_app_file(State, App, AppDir);
false ->
- copy_directory(AppDir, TargetDir, IncludeSrc),
- rewrite_app_file(App, TargetDir)
+ copy_directory(State, App, AppDir, TargetDir, IncludeSrc),
+ rewrite_app_file(State, App, TargetDir)
end.
%% If excluded apps exist in this App's applications list we must write a new .app
-rewrite_app_file(App, TargetDir) ->
+rewrite_app_file(State, 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.
+ {ok, [{application, AppName, AppData0}]} = file:consult(AppFile),
+ OldActiveDeps = proplists:get_value(applications, AppData0, []),
+ OldIncludedDeps = proplists:get_value(included_applications, AppData0, []),
+ OldModules = proplists:get_value(modules, AppData0, []),
+ ExcludedModules = proplists:get_value(Name,
+ rlx_state:exclude_modules(State), []),
+
+ %% maybe replace excluded apps
+ AppData2 =
+ case {OldActiveDeps, OldIncludedDeps} of
+ {ActiveDeps, IncludedDeps} ->
+ AppData0;
+ _ ->
+ AppData1 = lists:keyreplace(applications
+ ,1
+ ,AppData0
+ ,{applications, ActiveDeps}),
+ lists:keyreplace(included_applications
+ ,1
+ ,AppData1
+ ,{included_applications, IncludedDeps})
+ end,
+ %% maybe replace excluded modules
+ AppData3 =
+ case ExcludedModules of
+ [] -> AppData2;
+ _ ->
+ lists:keyreplace(modules
+ ,1
+ ,AppData2
+ ,{modules, OldModules -- ExcludedModules})
+ end,
+ Spec = io_lib:format("~p.\n", [{application, AppName, AppData3}]),
+ write_file_if_contents_differ(AppFile, Spec).
write_file_if_contents_differ(Filename, Bytes) ->
ToWrite = iolist_to_binary(Bytes),
@@ -275,8 +290,8 @@ link_directory(AppDir, TargetDir) ->
ok
end.
-copy_directory(AppDir, TargetDir, IncludeSrc) ->
- [copy_dir(AppDir, TargetDir, SubDir)
+copy_directory(State, App, AppDir, TargetDir, IncludeSrc) ->
+ [copy_dir(State, App, AppDir, TargetDir, SubDir)
|| SubDir <- ["ebin",
"include",
"priv",
@@ -289,13 +304,20 @@ copy_directory(AppDir, TargetDir, IncludeSrc) ->
[]
end]].
-copy_dir(AppDir, TargetDir, SubDir) ->
+copy_dir(State, App, AppDir, TargetDir, SubDir) ->
SubSource = filename:join(AppDir, SubDir),
SubTarget = filename:join(TargetDir, SubDir),
case ec_file:is_dir(SubSource) of
true ->
ok = rlx_util:mkdir_p(SubTarget),
- case ec_file:copy(SubSource, SubTarget, [recursive]) of
+ %% get a list of the modules to be excluded from this app
+ AppName = rlx_app_info:name(App),
+ ExcludedModules = proplists:get_value(AppName, rlx_state:exclude_modules(State),
+ []),
+ ExcludedFiles = [filename:join([binary_to_list(SubSource),
+ atom_to_list(M) ++ ".beam"]) ||
+ M <- ExcludedModules],
+ case copy_dir(SubSource, SubTarget, ExcludedFiles) of
{error, E} ->
?RLX_ERROR({ec_file_error, AppDir, SubTarget, E});
ok ->
@@ -305,6 +327,22 @@ copy_dir(AppDir, TargetDir, SubDir) ->
ok
end.
+%% no files are excluded, just copy the whole dir
+copy_dir(SourceDir, TargetDir, []) ->
+ case ec_file:copy(SourceDir, TargetDir, [recursive]) of
+ {error, E} -> {error, E};
+ ok ->
+ ok
+ end;
+copy_dir(SourceDir, TargetDir, ExcludeFiles) ->
+ SourceFiles = filelib:wildcard(
+ filename:join([binary_to_list(SourceDir), "*"])),
+ lists:foreach(fun(F) ->
+ ok = ec_file:copy(F,
+ filename:join([TargetDir,
+ filename:basename(F)]))
+ end, SourceFiles -- ExcludeFiles).
+
create_release_info(State0, Release0, OutputDir) ->
RelName = atom_to_list(rlx_release:name(Release0)),
ReleaseDir = rlx_util:release_output_dir(State0, Release0),
diff --git a/src/rlx_state.erl b/src/rlx_state.erl
index 6974d52..75a5cba 100644
--- a/src/rlx_state.erl
+++ b/src/rlx_state.erl
@@ -82,7 +82,9 @@
upfrom/1,
upfrom/2,
format/1,
- format/2]).
+ format/2,
+ exclude_modules/1,
+ exclude_modules/2]).
-export_type([t/0,
@@ -107,6 +109,7 @@
overrides=[] :: [{AppName::atom(), Directory::file:filename()}],
skip_apps=[] :: [AppName::atom()],
exclude_apps=[] :: [AppName::atom()],
+ exclude_modules=[] :: [{App::atom(), [Module::atom()]}],
debug_info=keep :: keep | strip,
configured_releases :: releases(),
realized_releases :: releases(),
@@ -200,6 +203,15 @@ exclude_apps(#state_t{exclude_apps=Apps}) ->
exclude_apps(State, SkipApps) ->
State#state_t{exclude_apps=SkipApps}.
+-spec exclude_modules(t()) -> [{App::atom(), [Module::atom()]}].
+exclude_modules(#state_t{exclude_modules=Modules}) ->
+ Modules.
+
+%% @doc modules to be excluded from the release
+-spec exclude_modules(t(), [{App::atom(), [Module::atom()]}]) -> t().
+exclude_modules(State, SkipModules) ->
+ State#state_t{exclude_modules=SkipModules}.
+
-spec debug_info(t()) -> keep | strip.
debug_info(#state_t{debug_info=DebugInfo}) ->
DebugInfo.
diff --git a/test/rlx_release_SUITE.erl b/test/rlx_release_SUITE.erl
index a017297..c9430fd 100644
--- a/test/rlx_release_SUITE.erl
+++ b/test/rlx_release_SUITE.erl
@@ -55,7 +55,8 @@
make_included_nodetool_release/1,
make_not_included_nodetool_release/1,
make_src_release/1,
- make_excluded_src_release/1]).
+ make_excluded_src_release/1,
+ make_exclude_modules_release/1]).
-include_lib("common_test/include/ct.hrl").
-include_lib("eunit/include/eunit.hrl").
@@ -91,7 +92,7 @@ all() ->
make_config_script_release, make_release_twice, make_release_twice_dev_mode,
make_erts_release, make_erts_config_release,
make_included_nodetool_release, make_not_included_nodetool_release,
- make_src_release, make_excluded_src_release].
+ make_src_release, make_excluded_src_release, make_exclude_modules_release].
add_providers(Config) ->
LibDir1 = proplists:get_value(lib1, Config),
@@ -1401,6 +1402,46 @@ make_excluded_src_release(Config) ->
?assert(not ec_file:exists(filename:join([OutputDir, "foo", "lib",
"goal_app_1-0.0.1", "src"]))).
+%% Test to ensure that excluded modules don't end up in the release
+make_exclude_modules_release(Config) ->
+ LibDir1 = proplists:get_value(lib1, Config),
+
+ rlx_test_utils:create_app(LibDir1, "goal_app_1", "0.0.1", [stdlib,kernel, non_goal_1], []),
+ rlx_test_utils:create_app(LibDir1, "non_goal_1", "0.0.1", [stdlib,kernel], []),
+
+ ConfigFile = filename:join([LibDir1, "relx.config"]),
+ rlx_test_utils:write_config(ConfigFile,
+ [{release, {foo, "0.0.1"},
+ [goal_app_1]},
+ {exclude_modules, [{non_goal_1, [a_real_beamnon_goal_1]}]}]),
+ OutputDir = filename:join([proplists:get_value(priv_dir, Config),
+ rlx_test_utils:create_random_name("relx-output")]),
+ {ok, Cwd} = file:get_cwd(),
+ {ok, State} = relx:do(Cwd, undefined, undefined, [], [LibDir1], 3,
+ OutputDir, [],
+ ConfigFile),
+ [{{foo, "0.0.1"}, Release}] = ec_dictionary:to_list(rlx_state:realized_releases(State)),
+ AppSpecs = rlx_release:applications(Release),
+ ?assert(lists:keymember(stdlib, 1, AppSpecs)),
+ ?assert(lists:keymember(kernel, 1, AppSpecs)),
+ ?assert(lists:member({goal_app_1, "0.0.1"}, AppSpecs)),
+ %% ensure that the excluded module beam file didn't get copied
+ ?assert(not ec_file:exists(filename:join([OutputDir, "foo", "lib",
+ "non_goal_1-0.0.1", "ebin",
+ "a_real_beamnon_goal_1.beam"]))),
+
+ ?assertMatch({ok, [{application,non_goal_1,
+ [{description,[]},
+ {vsn,"0.0.1"},
+ {modules,[]},
+ {included_applications,[]},
+ {registered,[]},
+ {applications,[stdlib,kernel]}]}]},
+ file:consult(filename:join([OutputDir, "foo", "lib",
+ "non_goal_1-0.0.1", "ebin",
+ "non_goal_1.app"]))).
+
+
%%%===================================================================
%%% Helper Functions
%%%===================================================================
diff --git a/test/rlx_test_utils.erl b/test/rlx_test_utils.erl
index 3ddc134..d1b7fe4 100644
--- a/test/rlx_test_utils.erl
+++ b/test/rlx_test_utils.erl
@@ -6,27 +6,26 @@
create_app(Dir, Name, Vsn, Deps, LibDeps) ->
AppDir = filename:join([Dir, Name ++ "-" ++ Vsn]),
- write_app_file(AppDir, Name, Vsn, Deps, LibDeps),
+ write_app_file(AppDir, Name, Vsn, app_modules(Name), Deps, LibDeps),
write_src_file(AppDir, Name),
- write_beam_file(AppDir, Name),
+ compile_src_files(AppDir),
rlx_app_info:new(erlang:list_to_atom(Name), Vsn, AppDir,
Deps, []).
create_empty_app(Dir, Name, Vsn, Deps, LibDeps) ->
AppDir = filename:join([Dir, Name ++ "-" ++ Vsn]),
- write_app_file(AppDir, Name, Vsn, Deps, LibDeps),
+ write_app_file(AppDir, Name, Vsn, [], Deps, LibDeps),
rlx_app_info:new(erlang:list_to_atom(Name), Vsn, AppDir,
Deps, []).
-write_beam_file(Dir, Name) ->
- Beam = filename:join([Dir, "ebin", "not_a_real_beam" ++ Name ++ ".beam"]),
- ok = filelib:ensure_dir(Beam),
- ok = ec_file:write_term(Beam, testing_purposes_only).
+app_modules(Name) ->
+ [list_to_atom(M ++ Name) ||
+ M <- ["a_real_beam"]].
write_src_file(Dir, Name) ->
- Src = filename:join([Dir, "src", "not_a_real_beam" ++ Name ++ ".erl"]),
+ Src = filename:join([Dir, "src", "a_real_beam" ++ Name ++ ".erl"]),
ok = filelib:ensure_dir(Src),
- ok = ec_file:write_term(Src, testing_purposes_only).
+ ok = file:write_file(Src, beam_file_contents("a_real_beam"++Name)).
write_appup_file(AppInfo, DownVsn) ->
Dir = rlx_app_info:dir(AppInfo),
@@ -36,16 +35,27 @@ write_appup_file(AppInfo, DownVsn) ->
ok = filelib:ensure_dir(Filename),
ok = ec_file:write_term(Filename, {Vsn, [{DownVsn, []}], [{DownVsn, []}]}).
-write_app_file(Dir, Name, Version, Deps, LibDeps) ->
+write_app_file(Dir, Name, Version, Modules, Deps, LibDeps) ->
Filename = filename:join([Dir, "ebin", Name ++ ".app"]),
ok = filelib:ensure_dir(Filename),
- ok = ec_file:write_term(Filename, get_app_metadata(Name, Version, Deps, LibDeps)).
-
-get_app_metadata(Name, Vsn, Deps, LibDeps) ->
+ ok = ec_file:write_term(Filename, get_app_metadata(Name, Version, Modules,
+ Deps, LibDeps)).
+
+compile_src_files(Dir) ->
+ %% compile all *.erl files in src to ebin
+ SrcDir = filename:join([Dir, "src"]),
+ OutputDir = filename:join([Dir, "ebin"]),
+ lists:foreach(fun(SrcFile) ->
+ {ok, _} = compile:file(SrcFile, [{outdir, OutputDir},
+ return_errors])
+ end, ec_file:find(SrcDir, "\\.erl")),
+ ok.
+
+get_app_metadata(Name, Vsn, Modules, Deps, LibDeps) ->
{application, erlang:list_to_atom(Name),
[{description, ""},
{vsn, Vsn},
- {modules, []},
+ {modules, Modules},
{included_applications, LibDeps},
{registered, []},
{applications, Deps}]}.
@@ -63,6 +73,9 @@ write_config(Filename, Values) ->
ok = ec_file:write(Filename,
[io_lib:format("~p.\n", [Val]) || Val <- Values]).
+beam_file_contents(Name) ->
+ "-module("++Name++").".
+
test_template_contents() ->
"{erts_vsn, \"{{erts_vsn}}\"}.\n"
"{release_erts_version, \"{{release_erts_version}}\"}.\n"
@@ -113,4 +126,4 @@ list_to_term(String) ->
unescape_string(String) ->
re:replace(String, "\"", "",
- [global, {return, list}]). \ No newline at end of file
+ [global, {return, list}]).