Permalink
Browse files

Enable customization of generated boot scripts

Functions specified with the new parameter {boot_phase_fun, fun/4}
can be used to control the contents of boot files.
  • Loading branch information...
1 parent 38ca2e8 commit a67aa6b2690679896bf73bca7f81544a7adc7505 @hawk committed Nov 6, 2013
@@ -165,6 +165,23 @@
booting up.</p>
</item>
+ <tag><c>boot_phase_fun</c></tag>
+ <item>
+ <p>This is a user defined function which can be used for
+ customization of boot scripts. A boot script consists of the
+ following phases: <c>preloaded</c>,
+ <c>kernel_load_completed</c>, <c>modules_loaded</c>,
+ <c>init_kernel_started</c>, <c>applications_loaded</c> and
+ <c>applications_started</c>. The <c>boot_phase_fun</c> has the
+ following signature: <c>fun((rel_name(), rel_vsn(),
+ boot_phase_name(), [boot_script_cmd()]) ->
+ [boot_script_cmd()])</c>. It will be invoked once for each
+ phase with the release name, release version, boot phase name
+ and a list of boot script commands as arguments. The function
+ is intended to return a new list of boot script commands.
+ </p>
+ </item>
+
<tag><c>rel</c></tag>
<item>
<p>Release specific configuration. Each release maps to a
@@ -510,6 +527,7 @@ sys() = {root_dir, root_dir()}
| {mod_cond, mod_cond()}
| {incl_cond, incl_cond()}
| {boot_rel, boot_rel()}
+ | {boot_phase_fun, boot_phase_fun()}
| {rel, rel_name(), rel_vsn(), [rel_app()]}
| {relocatable, relocatable()}
| {app_file, app_file()}
@@ -549,6 +567,9 @@ app_type() = permanent | transient | temporary | load | none
app_vsn() = string()
archive_opt = zip_create_opt()
boot_rel() = rel_name()
+boot_phase_fun() = fun((rel_name(), rel_vsn(), boot_phase_name(), [boot_script_cmd()]) -> [term()])
+boot_phase_name() = atom()
+boot_script_cmd() = term()
app_file() = keep | strip | all
app_dir_vsn() = keep | strip
debug_info() = keep | strip
@@ -57,6 +57,9 @@
-type rel_name() :: string().
-type rel_vsn() :: string().
-type boot_rel() :: rel_name().
+-type boot_phase_fun() :: fun((rel_name(), rel_vsn(), boot_phase_name(), [boot_script_cmd()]) -> [boot_script_cmd()]).
+-type boot_phase_name() :: atom().
+-type boot_script_cmd() :: term().
-type rel_app() :: app_name()
| {app_name(), app_type()}
| {app_name(), [incl_app()]}
@@ -220,9 +223,9 @@
-record(rel,
{
- name :: rel_name(),
- vsn :: rel_vsn(),
- rel_apps :: [#rel_app{}]
+ name :: rel_name(),
+ vsn :: rel_vsn(),
+ rel_apps :: [#rel_app{}]
}).
-record(sys,
@@ -236,6 +239,7 @@
%% Target cond
boot_rel :: boot_rel(),
+ boot_phase_fun :: boot_phase_fun() | undefined,
rels :: [#rel{}],
emu_name :: emu_name(),
profile :: profile(),
@@ -1398,6 +1398,8 @@ decode(#sys{} = Sys, [{Key, Val} | KeyVals]) ->
Sys#sys{incl_cond = Val};
boot_rel when is_list(Val) ->
Sys#sys{boot_rel = Val};
+ boot_phase_fun when is_function(Val, 4) ->
+ Sys#sys{boot_phase_fun = Val};
emu_name when is_list(Val) ->
Sys#sys{emu_name = Val};
profile when Val =:= development;
@@ -1573,7 +1575,7 @@ decode(#rel{rel_apps = RelApps} = Rel, [RelApp | KeyVals]) ->
end,
case ValidTypesAssigned of
true ->
- decode(Rel#rel{rel_apps = RelApps ++ [RA]}, KeyVals);
+ decode(Rel#rel{rel_apps = [RA | RelApps]}, KeyVals);
false ->
reltool_utils:throw_error("Illegal option: ~p", [RelApp])
end;
@@ -424,7 +424,7 @@ gen_script(Rel, Sys, PathFlag, Variables) ->
end.
do_gen_script(#rel{name = RelName, vsn = RelVsn},
- #sys{apps = Apps} = Sys,
+ #sys{apps = Apps, boot_phase_fun = PhaseFun} = Sys,
MergedApps,
PathFlag,
Variables) ->
@@ -439,45 +439,66 @@ do_gen_script(#rel{name = RelName, vsn = RelVsn},
MergedApps),
%% Create the script
- DeepList =
+ Phases =
[
%% Register preloaded modules
- {preLoaded, lists:sort(Preloaded)},
- {progress, preloaded},
+ {preloaded,
+ [
+ {preLoaded, lists:sort(Preloaded)}
+ ]},
+
%% Load mandatory modules
- {path, create_mandatory_path(Sys, MergedApps, PathFlag, Variables)},
- {primLoad, lists:sort(Mandatory)},
- {kernel_load_completed},
- {progress, kernel_load_completed},
+ {kernel_load_completed,
+ [
+ {path, create_mandatory_path(Sys, MergedApps, PathFlag, Variables)},
+ {primLoad, lists:sort(Mandatory)},
+ {kernel_load_completed}
+ ]},
%% Load remaining modules
- [load_app_mods(Sys, A, Early, PathFlag, Variables) || A <- MergedApps],
- {progress, modules_loaded},
+ {modules_loaded,
+ [load_app_mods(Sys, A, Early, PathFlag, Variables) || A <- MergedApps]
+ },
%% Start kernel processes
- {path, create_path(Sys, MergedApps, PathFlag, Variables)},
- kernel_processes(gen_app(KernelApp)),
- {progress, init_kernel_started},
+ {init_kernel_started,
+ [
+ {path, create_path(Sys, MergedApps, PathFlag, Variables)},
+ kernel_processes(gen_app(KernelApp))
+ ]},
%% Load applications
- [{apply, {application, load, [gen_app(A)]}} ||
- A = #app{name = Name, app_type = Type} <- MergedApps,
- Name =/= kernel,
- Type =/= none],
- {progress, applications_loaded},
+ {applications_loaded,
+ [{apply, {application, load, [gen_app(A)]}} ||
+ A = #app{name = Name, app_type = Type} <- MergedApps,
+ Name =/= kernel,
+ Type =/= none]},
%% Start applications
- [{apply, {application, start_boot, [Name, Type]}} ||
- #app{name = Name, app_type = Type} <- MergedApps,
- Type =/= none,
- Type =/= load,
- not lists:member(Name, InclApps)],
+ {applications_started,
+ [{apply, {application, start_boot, [Name, Type]}} ||
+ #app{name = Name, app_type = Type} <- MergedApps,
+ Type =/= none,
+ Type =/= load,
+ not lists:member(Name, InclApps)]
+ },
%% Apply user specific customizations
- {apply, {c, erlangrc, []}},
- {progress, started}
+ {started,
+ [
+ {apply, {c, erlangrc, []}}]}
],
- {ok, {script, {RelName, RelVsn}, lists:flatten(DeepList)}}.
+ {ok, {script,
+ {RelName, RelVsn},
+ filter_phases(RelName, RelVsn, PhaseFun, Phases)}}.
+
+filter_phases(RelName, RelVsn, undefined, Phases) ->
+ Fun = fun(_RelName, _RelVsn, _Phase, Items) -> Items end,
+ filter_phases(RelName, RelVsn, Fun, Phases);
+filter_phases(RelName, RelVsn, Fun, Phases) when is_function(Fun, 4) ->
+ lists:flatten([[Fun(RelName, RelVsn, Phase, lists:flatten(Items)),
+ {progress, Phase}] ||
+ {Phase, Items} <- Phases]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -106,6 +106,7 @@ all() ->
create_release,
create_release_sort,
create_script,
+ custom_script,
create_script_sort,
create_target,
app_dir_vsn,
@@ -554,6 +555,55 @@ create_script(_Config) ->
?m(ok, reltool:stop(Pid)),
ok.
+custom_script(_Config) ->
+ %% Configure the server
+ RelName1 = "Customized boot script",
+ RelName2 = "Yet another boot script",
+ RelName3 = "As is",
+ RelVsn = "1.0",
+ Load = {apply,{custom,load,[]}},
+ Start = {apply,{custom,start,[]}},
+ ErlangRc = {apply,{c,erlangrc,[]}},
+ PhaseFun =
+ fun(Rel, _Vsn, Phase, Cmds) ->
+ case Phase of
+ applications_loaded when Rel =:= RelName2 ->
+ Cmds ++ [Load];
+ applications_started when Rel =:= RelName1 ->
+ Cmds ++ [Start];
+ started when Rel =:= RelName1; Rel =:= RelName2 ->
+ Cmds -- [ErlangRc];
+ _ ->
+ Cmds
+ end
+ end,
+ Config =
+ {sys,
+ [
+ {lib_dirs, []},
+ {boot_rel, RelName1},
+ {boot_phase_fun, PhaseFun},
+ {rel, RelName1, RelVsn, [stdlib, kernel]},
+ {rel, RelName2, RelVsn, [stdlib, kernel]},
+ {rel, RelName3, RelVsn, [stdlib, kernel]}
+ ]},
+ {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Config}])),
+
+ %% Generate script files
+ {ok, {script,_,Cmds1}} = ?msym({ok, _}, reltool:get_script(Pid, RelName1)),
+ {ok, {script,_,Cmds2}} = ?msym({ok, _}, reltool:get_script(Pid, RelName2)),
+ {ok, {script,_,Cmds3}} = ?msym({ok, _}, reltool:get_script(Pid, RelName3)),
+
+ %% Verify that the customizations are performed
+ ?m([ErlangRc], Cmds3 -- Cmds1),
+ ?m([ErlangRc], Cmds3 -- Cmds2),
+ ?m([Start], Cmds1 -- Cmds3),
+ ?m([Load], Cmds2 -- Cmds3),
+
+ %% Stop server
+ ?m(ok, reltool:stop(Pid)),
+ ok.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Test creation of .script with different sorting of applications and
%% included applications.

0 comments on commit a67aa6b

Please sign in to comment.