aboutsummaryrefslogtreecommitdiffstats
path: root/lib/sasl/src/systools_make.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sasl/src/systools_make.erl')
-rw-r--r--lib/sasl/src/systools_make.erl2155
1 files changed, 2155 insertions, 0 deletions
diff --git a/lib/sasl/src/systools_make.erl b/lib/sasl/src/systools_make.erl
new file mode 100644
index 0000000000..20a142c763
--- /dev/null
+++ b/lib/sasl/src/systools_make.erl
@@ -0,0 +1,2155 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(systools_make).
+
+%% Purpose : Create start script. RelName.rel --> RelName.{script,boot}.
+%% and create a tar file of a release (RelName.tar.gz)
+
+-export([make_script/1, make_script/2, make_script/3,
+ make_tar/1, make_tar/2]).
+
+-export([format_error/1, format_warning/1]).
+
+-export([read_release/2, get_release/2, get_release/3,
+ get_release/4, pack_app/1]).
+
+-export([read_application/4]).
+
+-import(lists, [filter/2, keysort/2, keysearch/3, map/2, reverse/1,
+ append/1, foldl/3, member/2, foreach/2]).
+
+-include("systools.hrl").
+
+-include_lib("kernel/include/file.hrl").
+
+-define(XREF_SERVER, systools_make).
+
+-compile({inline,[{badarg,2}]}).
+
+%%-----------------------------------------------------------------
+%% Create a boot script from a release file.
+%% Options is a list of {path, Path} | silent | local where path sets
+%% the search path, silent supresses error message printing on console,
+%% local generates a script with references to the directories there
+%% the applications are found.
+%%
+%% New options: {path,Path} can contain wildcards
+%% no_module_tests
+%% {variables,[{Name,AbsString}]}
+%% {machine, jam | beam | vee}
+%% exref | {exref, [AppName]}
+%%-----------------------------------------------------------------
+
+make_script(RelName) when is_list(RelName) ->
+ make_script(RelName, []);
+make_script(RelName) ->
+ badarg(RelName,[RelName]).
+
+make_script(RelName, Flags) when is_list(RelName), is_list(Flags) ->
+ case get_outdir(Flags) of
+ "" ->
+ make_script(RelName, RelName, Flags);
+ OutDir ->
+ %% To maintain backwards compatibility for make_script/3,
+ %% the boot script file name is constructed here, before
+ %% checking the validity of OutDir
+ %% (is done in check_args_script/1)
+ Output = filename:join(OutDir, filename:basename(RelName)),
+ make_script(RelName, Output, Flags)
+ end.
+
+make_script(RelName, Output, Flags) when is_list(RelName),
+ is_list(Output),
+ is_list(Flags) ->
+ case check_args_script(Flags) of
+ [] ->
+ Path0 = get_path(Flags),
+ Path1 = mk_path(Path0), % expand wildcards etc.
+ Path = make_set(Path1 ++ code:get_path()),
+ ModTestP = {not member(no_module_tests, Flags),
+ xref_p(Flags)},
+ case get_release(RelName, Path, ModTestP, machine(Flags)) of
+ {ok, Release, Appls, Warnings} ->
+ case generate_script(Output,Release,Appls,Flags) of
+ ok ->
+ return(ok,Warnings,Flags);
+ Error ->
+ return(Error,Warnings,Flags)
+ end;
+ Error ->
+ return(Error,[],Flags)
+ end;
+ ErrorVars ->
+ badarg(ErrorVars, [RelName, Flags])
+ end;
+
+make_script(RelName, _Output, Flags) when is_list(Flags) ->
+ badarg(RelName,[RelName, Flags]);
+make_script(RelName, _Output, Flags) ->
+ badarg(Flags,[RelName, Flags]).
+
+%% Inlined.
+badarg(BadArg, Args) ->
+ erlang:error({badarg,BadArg}, Args).
+
+machine(Flags) ->
+ case get_flag(machine,Flags) of
+ {machine, Machine} when is_atom(Machine) -> Machine;
+ _ -> false
+ end.
+
+get_path(Flags) ->
+ case get_flag(path,Flags) of
+ {path,Path} when is_list(Path) -> Path;
+ _ -> []
+ end.
+
+get_outdir(Flags) ->
+ case get_flag(outdir,Flags) of
+ {outdir,OutDir} when is_list(OutDir) ->
+ OutDir;
+ _ -> % false | {outdir, Badarg}
+ ""
+ end.
+
+return(ok,Warnings,Flags) ->
+ case member(silent,Flags) of
+ true ->
+ {ok,?MODULE,Warnings};
+ _ ->
+ io:format("~s",[format_warning(Warnings)]),
+ ok
+ end;
+return({error,Mod,Error},_,Flags) ->
+ case member(silent,Flags) of
+ true ->
+ {error,Mod,Error};
+ _ ->
+ io:format("~s",[Mod:format_error(Error)]),
+ error
+ end.
+
+%%-----------------------------------------------------------------
+%% Create a release package from a release file.
+%% Options is a list of {path, Path} | silent |
+%% {dirs, [src,include,examples,..]} | {erts, ErtsDir} where path
+%% sets the search path, silent supresses error message printing,
+%% dirs includes the specified directories (per application) in the
+%% release package and erts specifies that the erts-Vsn/bin directory
+%% should be included in the release package and there it can be found.
+%%
+%% New options: {path,Path} can contain wildcards
+%% no_module_tests
+%% exref | {exref, [AppName]}
+%% {variables,[{Name,AbsString}]}
+%% {machine, jam | beam | vee}
+%% {var_tar, include | ownfile | omit}
+%%
+%% The tar file contains:
+%% lib/App-Vsn/ebin
+%% /priv
+%% [/src]
+%% [/include]
+%% [/doc]
+%% [/examples]
+%% [/...]
+%% Variable1.tar.gz
+%% ...
+%% VariableN.tar.gz
+%% releases/RelName.rel
+%% RelVsn/start.boot
+%% relup
+%% sys.config
+%% erts-EVsn[/bin]
+%%-----------------------------------------------------------------
+
+make_tar(RelName) when is_list(RelName) ->
+ make_tar(RelName, []);
+make_tar(RelName) ->
+ badarg(RelName,[RelName]).
+
+make_tar(RelName, Flags) when is_list(RelName), is_list(Flags) ->
+ case check_args_tar(Flags) of
+ [] ->
+ Path0 = get_path(Flags),
+ Path1 = mk_path(Path0),
+ Path = make_set(Path1 ++ code:get_path()),
+ ModTestP = {not member(no_module_tests, Flags),
+ xref_p(Flags)},
+ case get_release(RelName, Path, ModTestP, machine(Flags)) of
+ {ok, Release, Appls, Warnings} ->
+ case catch mk_tar(RelName, Release, Appls, Flags, Path1) of
+ ok ->
+ return(ok,Warnings,Flags);
+ Error ->
+ return(Error,Warnings,Flags)
+ end;
+ Error ->
+ return(Error,[],Flags)
+ end;
+ ErrorVars ->
+ badarg(ErrorVars, [RelName, Flags])
+ end;
+make_tar(RelName, Flags) when is_list(Flags) ->
+ badarg(RelName,[RelName, Flags]);
+make_tar(RelName, Flags) ->
+ badarg(Flags,[RelName, Flags]).
+
+%%______________________________________________________________________
+%% get_release(File, Path) ->
+%% get_release(File, Path, ModTestP) ->
+%% get_release(File, Path, ModTestP, Machine) ->
+%% {ok, #release, [{{Name,Vsn},#application}], Warnings} | {error, What}
+
+get_release(File, Path) ->
+ get_release(File, Path, true, false).
+
+get_release(File, Path, ModTestP) ->
+ get_release(File, Path, ModTestP, false).
+
+get_release(File, Path, ModTestP, Machine) ->
+ case catch get_release1(File, Path, ModTestP, Machine) of
+ {error, Error} ->
+ {error, ?MODULE, Error};
+ {'EXIT', Why} ->
+ {error, ?MODULE, {'EXIT',Why}};
+ Answer ->
+ Answer
+ end.
+
+get_release1(File, Path, ModTestP, Machine) ->
+ {ok, Release} = read_release(File, Path),
+ {ok, Appls0} = collect_applications(Release, Path),
+ {ok, Appls1} = check_applications(Appls0),
+ {ok, Appls2} = sort_included_applications(Appls1, Release), % OTP-4121
+ {ok, Warnings} = check_modules(Appls2, Path, ModTestP, Machine),
+ {ok, Appls} = sort_appls(Appls2),
+ {ok, Release, Appls, Warnings}.
+
+%%______________________________________________________________________
+%% read_release(File, Path) -> {ok, #release} | throw({error, What})
+
+read_release(File, Path) ->
+ case read_file(File ++ ".rel", ["."|Path]) of
+ {ok, Release, _FullName} ->
+ check_rel(Release);
+ {error,Error} ->
+ throw({error,?MODULE,Error})
+ end.
+
+check_rel(Release) ->
+ case catch check_rel1(Release) of
+ {ok, {Name,Vsn,Evsn,Appl,Incl}} ->
+ {ok, #release{name=Name, vsn=Vsn,
+ erts_vsn=Evsn,
+ applications=Appl,
+ incl_apps=Incl}};
+ {error, Error} ->
+ throw({error,?MODULE,Error});
+ Error ->
+ throw({error,?MODULE,Error})
+ end.
+
+check_rel1({release,{Name,Vsn},{erts,EVsn},Appl}) when is_list(Appl) ->
+ check_name(Name),
+ check_vsn(Vsn),
+ check_evsn(EVsn),
+ {Appls,Incls} = check_appl(Appl),
+ {ok, {Name,Vsn,EVsn,Appls,Incls}};
+check_rel1(_) ->
+ {error, badly_formatted_release}.
+
+check_name(Name) ->
+ case string_p(Name) of
+ true ->
+ Name;
+ _ ->
+ throw({error,{illegal_name, Name}})
+ end.
+
+check_vsn(Vsn) ->
+ case string_p(Vsn) of
+ true ->
+ Vsn;
+ _ ->
+ throw({error,{illegal_form, Vsn}})
+ end.
+
+check_evsn(Vsn) ->
+ case string_p(Vsn) of
+ true ->
+ Vsn;
+ _ ->
+ throw({error,{illegal_form, {erts,Vsn}}})
+ end.
+
+check_appl(Appl) ->
+ case filter(fun({App,Vsn}) when is_atom(App) ->
+ not string_p(Vsn);
+ ({App,Vsn,Incl}) when is_atom(App), is_list(Incl) ->
+ case {string_p(Vsn), a_list_p(Incl)} of
+ {true, true} -> false;
+ _ -> true
+ end;
+ ({App,Vsn,Type}) when is_atom(App), is_atom(Type) ->
+ case {string_p(Vsn), is_app_type(Type)} of
+ {true, true} -> false;
+ _ -> true
+ end;
+ ({App,Vsn,Type,Incl}) when is_atom(App),
+ is_atom(Type),
+ is_list(Incl) ->
+ case {string_p(Vsn),is_app_type(Type),a_list_p(Incl)} of
+ {true, true, true} -> false;
+ _ -> true
+ end;
+ (_) ->
+ true
+ end,
+ Appl) of
+ [] ->
+ mandatory_applications(Appl),
+ split_app_incl(Appl);
+ Illegal ->
+ throw({error, {illegal_applications,Illegal}})
+ end.
+
+mandatory_applications(Appl) ->
+ AppNames = map(fun(AppT) -> element(1, AppT) end,
+ Appl),
+ Mand = mandatory_applications(),
+ case filter(fun(X) -> member(X, AppNames) end, Mand) of
+ Mand ->
+ ok;
+ _ ->
+ throw({error, {missing_mandatory_app, Mand}})
+ end.
+
+mandatory_applications() ->
+ [kernel, stdlib].
+
+split_app_incl(Appl) -> split_app_incl(Appl, [], []).
+
+split_app_incl([{App,Vsn}|Appls], Apps, Incls) ->
+ split_app_incl(Appls, [{App,Vsn,permanent}|Apps], Incls);
+split_app_incl([{App,Vsn,Incl}|Appls], Apps,Incls) when is_list(Incl) ->
+ split_app_incl(Appls, [{App,Vsn,permanent}|Apps], [{App,Incl}|Incls]);
+split_app_incl([{App,Vsn,Type}|Appls], Apps, Incls) ->
+ split_app_incl(Appls, [{App,Vsn,Type}|Apps], Incls);
+split_app_incl([{App,Vsn,Type,Incl}|Appls], Apps, Incls) when is_list(Incl) ->
+ split_app_incl(Appls, [{App,Vsn,Type}|Apps], [{App,Incl}|Incls]);
+split_app_incl([], Apps, Incls) ->
+ {reverse(Apps),reverse(Incls)}.
+
+%%______________________________________________________________________
+%% collect_applications(#release, Path) ->
+%% {ok,[{{Name,Vsn},#application}]} |
+%% throw({error, What})
+%% Read all the application files specified in the release descriptor
+
+collect_applications(Release, Path) ->
+ Appls = Release#release.applications,
+ Incls = Release#release.incl_apps,
+ X = foldl(fun({Name,Vsn,Type}, {Ok, Errs}) ->
+ case read_application(to_list(Name), Vsn, Path, Incls) of
+ {ok, A} ->
+ case {A#application.name,A#application.vsn} of
+ {Name,Vsn} ->
+ {[{{Name,Vsn}, A#application{type=Type}} | Ok],
+ Errs};
+ E ->
+ {Ok, [{bad_application_name, {Name, E}} | Errs]}
+ end;
+ {error, What} ->
+ {Ok, [{error_reading, {Name, What}} | Errs]}
+ end
+ end, {[],[]}, Appls),
+ case X of
+ {A, []} ->
+ {ok, reverse(A)};
+ {_, Errs} ->
+ throw({error, Errs})
+ end.
+
+
+%%______________________________________________________________________
+%% read_application(Name, Vsn, Path, Incls) -> {ok, #release} | {error, What}
+
+read_application(Name, Vsn, Path, Incls) ->
+ read_application(Name, Vsn, Path, Incls, false, no_fault).
+
+read_application(Name, Vsn, [Dir|Path], Incls, Found, FirstError) ->
+ case read_file(Name ++ ".app", [Dir]) of
+ {ok, Term, FullName} ->
+ case parse_application(Term, FullName, Vsn, Incls) of
+ {error, {no_valid_version, {Vsn, OtherVsn}}} when FirstError == no_fault ->
+ NFE = {no_valid_version, {{"should be", Vsn},
+ {"found file", filename:join(Dir, Name++".app"),
+ OtherVsn}}},
+ read_application(Name, Vsn, Path, Incls, true, NFE);
+ {error, {no_valid_version, {Vsn, _OtherVsn}}} ->
+ read_application(Name, Vsn, Path, Incls, true, FirstError);
+ Res ->
+ Res
+ end;
+ {error, {parse, _File, {Line, _Mod, Err}}} when FirstError == no_fault ->
+ read_application(Name, Vsn, Path, Incls, Found,
+ {parse_error, {filename:join(Dir, Name++".app"), Line, Err}});
+ {error, {parse, _File, _Err}} ->
+ read_application(Name, Vsn, Path, Incls, Found, FirstError);
+ {error, _Err} -> %% Not found
+ read_application(Name, Vsn, Path, Incls, Found, FirstError)
+ end;
+read_application(Name, Vsn, [], _, true, no_fault) ->
+ {error, {application_vsn, {Name,Vsn}}};
+read_application(_Name, _Vsn, [], _, true, FirstError) ->
+ {error, FirstError};
+read_application(Name, _, [], _, _, no_fault) ->
+ {error, {not_found, Name ++ ".app"}};
+read_application(_Name, _, [], _, _, FirstError) ->
+ {error, FirstError}.
+
+parse_application({application, Name, Dict}, File, Vsn, Incls)
+ when is_atom(Name),
+ is_list(Dict) ->
+ Items = [vsn,id,description,modules,registered,
+ applications,included_applications,mod,start_phases,env,maxT,maxP],
+ case catch get_items(Items, Dict) of
+ [Vsn,Id,Desc,Mods,Regs,Apps,Incs0,Mod,Phases,Env,MaxT,MaxP] ->
+ case override_include(Name, Incs0, Incls) of
+ {ok, Incs} ->
+ {ok, #application{name=Name,
+ vsn=Vsn,
+ id=Id,
+ description=Desc,
+ modules=Mods,
+ uses=Apps,
+ includes=Incs,
+ regs=Regs,
+ mod=Mod,
+ start_phases=Phases,
+ env=Env,
+ maxT=MaxT,
+ maxP=MaxP,
+ dir=filename:dirname(File)}};
+ {error, IncApps} ->
+ {error, {override_include, IncApps}}
+ end;
+ [OtherVsn,_,_,_,_,_,_,_,_,_,_,_] ->
+ {error, {no_valid_version, {Vsn, OtherVsn}}};
+ Err ->
+ {error, {Err, {application, Name, Dict}}}
+ end;
+parse_application(Other, _, _, _) ->
+ {error, {badly_formatted_application, Other}}.
+
+%% Test if all included applications specifed in the .rel file
+%% exists in the {included_applications,Incs} specified in the
+%% .app file.
+override_include(Name, Incs, Incls) ->
+ case keysearch(Name, 1, Incls) of
+ {value, {Name, I}} ->
+ case specified(I, Incs) of
+ [] ->
+ {ok, I};
+ NotSpec ->
+ {error, NotSpec}
+ end;
+ _ ->
+ {ok, Incs}
+ end.
+
+specified([App|Incls], Spec) ->
+ case member(App, Spec) of
+ true ->
+ specified(Incls, Spec);
+ _ ->
+ [App|specified(Incls, Spec)]
+ end;
+specified([], _) ->
+ [].
+
+get_items([H|T], Dict) ->
+ Item = check_item(keysearch(H, 1, Dict),H),
+ [Item|get_items(T, Dict)];
+get_items([], _Dict) ->
+ [].
+
+check_item({_,{mod,{M,A}}},_) when is_atom(M) ->
+ {M,A};
+check_item({_,{vsn,Vsn}},I) ->
+ case string_p(Vsn) of
+ true -> Vsn;
+ _ -> throw({bad_param, I})
+ end;
+check_item({_,{id,Id}},I) ->
+ case string_p(Id) of
+ true -> Id;
+ _ -> throw({bad_param, I})
+ end;
+check_item({_,{description,Desc}},I) ->
+ case string_p(Desc) of
+ true -> Desc;
+ _ -> throw({bad_param, I})
+ end;
+check_item({_,{applications,Apps}},I) ->
+ case a_list_p(Apps) of
+ true -> Apps;
+ _ -> throw({bad_param, I})
+ end;
+check_item({_,{included_applications,Apps}},I) ->
+ case a_list_p(Apps) of
+ true -> Apps;
+ _ -> throw({bad_param, I})
+ end;
+check_item({_,{registered,Regs}},I) ->
+ case a_list_p(Regs) of
+ true -> Regs;
+ _ -> throw({bad_param, I})
+ end;
+check_item({_,{modules,Mods}},I) ->
+ case mod_list_p(Mods) of
+ true -> Mods;
+ _ -> throw({bad_param, I})
+ end;
+check_item({_,{start_phases,Phase}},I) ->
+ case t_list_p(Phase) of
+ true -> Phase;
+ _ -> throw({bad_param, I})
+ end;
+check_item({_,{env,Env}},I) ->
+ case t_list_p(Env) of
+ true -> Env;
+ _ -> throw({bad_param, I})
+ end;
+check_item({_,{maxT,MaxT}},I) ->
+ case MaxT of
+ MaxT when is_integer(MaxT), MaxT > 0 -> MaxT;
+ infinity -> infinity;
+ _ -> throw({bad_param, I})
+ end;
+check_item({_,{maxP,MaxP}},I) ->
+ case MaxP of
+ MaxP when is_integer(MaxP), MaxP > 0 -> MaxP;
+ infinity -> infinity;
+ _ -> throw({bad_param, I})
+ end;
+check_item(false, included_applications) -> % optional !
+ [];
+check_item(false, mod) -> % mod is optional !
+ [];
+check_item(false, env) -> % env is optional !
+ [];
+check_item(false, id) -> % id is optional !
+ [];
+check_item(false, start_phases) -> % start_phases is optional !
+ undefined;
+check_item(false, maxT) -> % maxT is optional !
+ infinity;
+check_item(false, maxP) -> % maxP is optional !
+ infinity;
+check_item(_, Item) ->
+ throw({missing_param, Item}).
+
+%%______________________________________________________________________
+%% check_applications([{{Name,Vsn},#application}]) ->
+%% ok | throw({error, Error})
+%% check that all referenced applications exists and that no
+%% application register processes with the same name.
+%% Check that included_applications are not specified as used
+%% in another application.
+
+check_applications(Appls) ->
+ undef_appls(Appls),
+ dupl_regs(Appls),
+ %% Make a list Incs = [{Name,App,AppVsn,Dir}]
+ Incs = [{IncApp,App,Appv,A#application.dir} ||
+ {{App,Appv},A} <- Appls,
+ IncApp <- A#application.includes],
+ dupl_incls(Incs),
+ Res = add_top_apps_to_uses(Incs, Appls, []),
+ {ok, Res}.
+
+
+
+undef_appls(Appls) ->
+ case undefined_applications(Appls) of
+ [] ->
+ ok;
+ L ->
+ throw({error, {undefined_applications, make_set(L)}})
+ end.
+
+dupl_regs(Appls) ->
+ %% Make a list Regs = [{Name,App,AppVsn,Dir}]
+ Regs = [{Name,App,Appv,A#application.dir} ||
+ {{App,Appv},A} <- Appls,
+ Name <- A#application.regs],
+ case duplicates(Regs) of
+ [] ->
+ ok;
+ Dups ->
+ throw({error, {duplicate_register, Dups}})
+ end.
+
+
+dupl_incls(Incs) ->
+ case duplicates(Incs) of
+ [] ->
+ ok;
+ Dups ->
+ throw({error, {duplicate_include, Dups}})
+ end.
+
+
+
+%% If an application uses another application which is included in yet
+%% another application, e.g. X uses A, A is included in T; then the A
+%% application in the X applications uses-variable is changed to the T
+%% application's top application to ensure the start order.
+%% Exception: if both X and A have the same top, then it is not
+%% added to avoid circular dependencies.
+%%
+%% add_top_apps_to_uses( list of all included applications in
+%% the system,
+%% list of all applications in the system,
+%% temporary result)
+%% -> new list of all applications
+add_top_apps_to_uses(_InclApps, [], Res) ->
+ %% InclApps = [{IncApp, App, AppVsn, Dir}]
+ Res;
+add_top_apps_to_uses(InclApps, [{Name,Appl} | Appls], Res) ->
+ MyTop = find_top_app(Appl#application.name, InclApps),
+ F = fun(UsedApp, AccIn) when UsedApp == MyTop ->
+ %% UW980513 This is a special case: The included app
+ %% uses its own top app. We'll allow it, but must
+ %% remove the top app from the uses list.
+ AccIn -- [MyTop];
+ (UsedApp, AccIn) ->
+ case lists:keysearch(UsedApp, 1, InclApps) of
+ false ->
+ AccIn;
+ {value, {_,DependApp,_,_}} ->
+ UsedAppTop = find_top_app(DependApp, InclApps),
+ case {lists:member(UsedAppTop, AccIn), MyTop} of
+ {true, _} ->
+ %% the top app is already in the uses
+ %% list, remove UsedApp
+ AccIn -- [UsedApp];
+ {_, UsedAppTop} ->
+ %% both are included in the same app
+ AccIn;
+ _ ->
+ %% change the used app to the used app's
+ %% top application
+ AccIn1 = AccIn -- [UsedApp],
+ AccIn1 ++ [UsedAppTop]
+ end
+ end
+ end,
+
+ NewUses = foldl(F, Appl#application.uses, Appl#application.uses),
+ add_top_apps_to_uses(InclApps, Appls,
+ Res++[{Name, Appl#application{uses = NewUses}}]).
+
+
+
+find_top_app(App, InclApps) ->
+ case lists:keysearch(App, 1, InclApps) of
+ false ->
+ App;
+ {value, {_,TopApp,_,_}} ->
+ find_top_app(TopApp, InclApps)
+ end.
+
+
+
+%%______________________________________________________________________
+%% undefined_applications([{{Name,Vsn},#application}]) ->
+%% [Name] list of applications that were declared in
+%% use declarations but are not contained in the release descriptor
+
+undefined_applications(Appls) ->
+ Uses = append(map(fun({_,A}) ->
+ A#application.uses ++ A#application.includes
+ end, Appls)),
+ Defined = map(fun({{X,_},_}) -> X end, Appls),
+ filter(fun(X) -> not member(X, Defined) end, Uses).
+
+%%______________________________________________________________________
+%% sort_included_applications(Applications, Release) -> Applications
+%% Applications = [{{Name,Vsn},#application}]
+%% Release = #release{}
+%%
+%% Check that included applications are given in the same order as in
+%% the release resource file (.rel). Otherwise load instructions in
+%% the boot script, and consequently release upgrade instructions in
+%% relup, may end up in the wrong order.
+
+sort_included_applications(Applications, Release) when is_tuple(Release) ->
+ {ok,
+ sort_included_applications(Applications, Release#release.applications)};
+
+sort_included_applications([{Tuple,Appl}|Appls], OrderedAppls) ->
+ case Appl#application.includes of
+ Incls when length(Incls)>1 ->
+ IndexedIncls = find_pos(Incls, OrderedAppls),
+ SortedIndexedIncls = lists:keysort(1, IndexedIncls),
+ Incls2 = lists:map(fun({_Index,Name}) -> Name end,
+ SortedIndexedIncls),
+ Appl2 = Appl#application{includes=Incls2},
+ [{Tuple,Appl2}|sort_included_applications(Appls, OrderedAppls)];
+ _Incls ->
+ [{Tuple,Appl}|sort_included_applications(Appls, OrderedAppls)]
+ end;
+sort_included_applications([], _OrderedAppls) ->
+ [].
+
+find_pos([Name|Incs], OrderedAppls) ->
+ [find_pos(1, Name, OrderedAppls)|find_pos(Incs, OrderedAppls)];
+find_pos([], _OrderedAppls) ->
+ [].
+
+find_pos(N, Name, [{Name,_Vsn,_Type}|_OrderedAppls]) ->
+ {N, Name};
+find_pos(N, Name, [_OtherAppl|OrderedAppls]) ->
+ find_pos(N+1, Name, OrderedAppls).
+
+%%______________________________________________________________________
+%% check_modules(Appls, Path, TestP, Machine) ->
+%% {ok, Warnings} | throw({error, What})
+%% where Appls = [{App,Vsn}, #application}]
+%% performs logical checking that we can find all the modules
+%% etc.
+
+check_modules(Appls, Path, TestP, Machine) ->
+ %% first check that all the module names are unique
+ %% Make a list M1 = [{Mod,Vsn,App,AppVsn,Dir}]
+ %% where Vsn = '$$ignore$$' | Specified
+ M1 = [{Mod,Vsn,App,Appv,A#application.dir} ||
+ {{App,Appv},A} <- Appls,
+ {Mod,Vsn} <- get_mod_vsn(A#application.modules)],
+ case duplicates(M1) of
+ [] ->
+ case check_mods(M1, Appls, Path, TestP, Machine) of
+ {error, Errors} ->
+ throw({error, {modules, Errors}});
+ Return ->
+ Return
+ end;
+ Dups ->
+% io:format("** ERROR Duplicate modules: ~p\n", [Dups]),
+ throw({error, {duplicate_modules, Dups}})
+ end.
+
+get_mod_vsn([{Mod,Vsn}|Mods]) ->
+ [{Mod,Vsn}|get_mod_vsn(Mods)];
+get_mod_vsn([Mod|Mods]) ->
+ [{Mod,'$$ignore$$'}|get_mod_vsn(Mods)];
+get_mod_vsn([]) ->
+ [].
+
+%%______________________________________________________________________
+%% Check that all modules exists and that the specified version
+%% corresponds to the version in the module's source code.
+%% Use the module extension of the running machine as extension for
+%% the checked modules.
+
+check_mods(Modules, Appls, Path, {true, XrefP}, Machine) ->
+ Ext = objfile_extension(Machine),
+ IncPath = create_include_path(Appls, Path),
+ Res = append(map(fun(ModT) ->
+ {Mod,_Vsn,App,_,Dir} = ModT,
+ case check_mod(Mod,App,Dir,Ext,IncPath) of
+ ok ->
+ [];
+ {error, Error} ->
+ [{error,{Error, ModT}}];
+ {warning, Warn} ->
+ [{warning,{Warn,ModT}}]
+ end
+ end,
+ Modules)),
+ Res2 = Res ++ check_xref(Appls, Path, XrefP),
+ case filter(fun({error, _}) -> true;
+ (_) -> false
+ end,
+ Res2) of
+ [] ->
+ {ok, filter(fun({warning, _}) -> true;
+ (_) -> false
+ end,
+ Res2)};
+ Errors ->
+ {error, Errors}
+ end;
+check_mods(_, _, _, _, _) ->
+ {ok, []}.
+
+check_xref(_Appls, _Path, false) ->
+ [];
+check_xref(Appls, Path, XrefP) ->
+ AppDirsL = [{App,A#application.dir} || {{App,_Appv},A} <- Appls],
+ AppDirs0 = sofs:relation(AppDirsL),
+ AppDirs = case XrefP of
+ true ->
+ AppDirs0;
+ {true, Apps} ->
+ sofs:restriction(AppDirs0, sofs:set(Apps))
+ end,
+ XrefArgs = [{xref_mode, modules}],
+ case catch xref:start(?XREF_SERVER, XrefArgs) of
+ {ok, _Pid} ->
+ ok;
+ {error, {already_started, _Pid}} ->
+ xref:stop(?XREF_SERVER), %% Clear out any previous data
+ xref:start(?XREF_SERVER, XrefArgs)
+ end,
+ {ok, _} = xref:set_default(?XREF_SERVER, verbose, false),
+ LibPath = case Path == code:get_path() of
+ true -> code_path; % often faster
+ false -> Path
+ end,
+ ok = xref:set_library_path(?XREF_SERVER, LibPath),
+ check_xref(sofs:to_external(AppDirs)).
+
+check_xref([{App,AppDir} | Appls]) ->
+ case xref:add_application(?XREF_SERVER, AppDir, {name,App}) of
+ {ok, _App} ->
+ check_xref(Appls);
+ Error ->
+ xref:stop(?XREF_SERVER),
+ [{error, Error}]
+ end;
+check_xref([]) ->
+ R = case xref:analyze(?XREF_SERVER, undefined_functions) of
+ {ok, []} ->
+ [];
+ {ok, Undefined} ->
+ %% This clause is a (temporary?) fix for hipe.
+ adjust_for_hipe(Undefined);
+ Error ->
+ [{error, Error}]
+ end,
+ xref:stop(?XREF_SERVER),
+ R.
+
+adjust_for_hipe(Undef) ->
+ case erlang:system_info(hipe_architecture) of
+ undefined ->
+ U = lists:filter(fun ({hipe_bifs,_,_}) -> false;
+ ({hipe,_,_}) -> false;
+ (_) -> true
+ end, Undef),
+ if
+ [] == U ->
+ [];
+ true ->
+ [{warning, {exref_undef, U}}]
+ end;
+ _Arch ->
+ %% Some BIFs are not always available on all versions of HiPE.
+ U = lists:filter(fun ({hipe_bifs,write_u64,2}) -> false;
+ (_) -> true
+ end, Undef),
+ [{warning, {exref_undef, U}}]
+ end.
+
+%% Perform cross reference checks between all modules specified
+%% in .app files.
+%%
+xref_p(Flags) ->
+ case member(exref, Flags) of
+ true ->
+ exists_xref(true);
+ _ ->
+ case get_flag(exref, Flags) of
+ {exref, Appls} when is_list(Appls) ->
+ case a_list_p(Appls) of
+ true -> exists_xref({true, Appls});
+ _ -> false
+ end;
+ _ ->
+ false
+ end
+ end.
+
+exists_xref(Flag) ->
+ case code:ensure_loaded(xref) of
+ {error, _} -> false;
+ _ -> Flag
+ end.
+
+objfile_extension(false) ->
+ code:objfile_extension();
+objfile_extension(Machine) ->
+ "." ++ atom_to_list(Machine).
+
+check_mod(Mod,App,Dir,Ext,IncPath) ->
+ ObjFile = mod_to_filename(Dir, Mod, Ext),
+ case file:read_file_info(ObjFile) of
+ {ok,FileInfo} ->
+ LastModTime = FileInfo#file_info.mtime,
+ check_module(Mod, Dir, LastModTime, IncPath);
+ _ ->
+ {error, {module_not_found, App, Mod}}
+ end.
+
+mod_to_filename(Dir, Mod, Ext) ->
+ Parts = packages:split(Mod),
+ filename:join([Dir | Parts]) ++ Ext.
+
+check_module(Mod, Dir, ObjModTime, IncPath) ->
+ {SrcDirs,_IncDirs}= smart_guess(Mod, Dir,IncPath),
+ case locate_src(Mod,SrcDirs) of
+ {ok,_FDir,_File,LastModTime} ->
+ if
+ LastModTime > ObjModTime ->
+ {warning, obj_out_of_date};
+ true ->
+ ok
+ end;
+ _ ->
+ {warning, source_not_found}
+ end.
+
+locate_src(Mod,[Dir|Dirs]) ->
+ File = filename:join(Dir, mod_to_fname(Mod) ++ ".erl"),
+ case file:read_file_info(File) of
+ {ok,FileInfo} ->
+ LastModTime = FileInfo#file_info.mtime,
+ {ok,Dir,File,LastModTime};
+ _ ->
+ locate_src(Mod,Dirs)
+ end;
+locate_src(_,[]) ->
+ false.
+
+mod_to_fname(Mod) ->
+ hd(lists:reverse(packages:split(Mod))).
+
+
+%%______________________________________________________________________
+%% smart_guess(Mod, Dir,IncludePath) -> {[Dirs],[IncDirs]}
+%% Guess the src code and include directory. If dir contains .../ebin
+%% src-dir should be one of .../src or .../src/e_src
+%% If dir does not contain .../ebin set dir to the same directory.
+
+smart_guess(Mod, Dir,IncPath) ->
+ case reverse(filename:split(Dir)) of
+ ["ebin"|D] ->
+ Subdirs = case packages:split(Mod) of
+ [_] -> [];
+ [_|_] = Parts ->
+ lists:reverse(tl(lists:reverse(Parts)))
+ end,
+ D1 = reverse(D),
+ Dirs = [filename:join(D1 ++ ["src" | Subdirs]),
+ filename:join(D1 ++ ["src", "e_src" | Subdirs])],
+ {Dirs,Dirs ++ IncPath};
+ _ ->
+ {[Dir],[Dir] ++ IncPath}
+ end.
+
+%%______________________________________________________________________
+%% generate_script(#release,
+%% [{{Name,Vsn},#application}], Flags) ->
+%% ok | {error, Error}
+%% Writes a script (a la magnus) to the file File.script
+%% and a bootfile to File.boot.
+
+generate_script(Output, Release, Appls, Flags) ->
+ PathFlag = path_flag(Flags),
+ Variables = get_variables(Flags),
+ Preloaded = preloaded(),
+ Mandatory = mandatory_modules(),
+ Script = {script, {Release#release.name,Release#release.vsn},
+ [{preLoaded, Preloaded},
+ {progress, preloaded},
+ {path, create_mandatory_path(Appls, PathFlag, Variables)},
+ {primLoad, Mandatory},
+ {kernel_load_completed},
+ {progress, kernel_load_completed}] ++
+ load_appl_mods(Appls, Mandatory ++ Preloaded,
+ PathFlag, Variables) ++
+ [{path, create_path(Appls, PathFlag, Variables)}] ++
+ create_kernel_procs(Appls) ++
+ create_load_appls(Appls) ++
+ create_start_appls(Appls) ++
+ script_end()
+ },
+
+ ScriptFile = Output ++ ".script",
+ case file:open(ScriptFile, [write]) of
+ {ok, Fd} ->
+ io:format(Fd, "%% script generated at ~w ~w\n~p.\n",
+ [date(), time(), Script]),
+ file:close(Fd),
+
+ BootFile = Output ++ ".boot",
+ case file:write_file(BootFile, term_to_binary(Script)) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ {error, ?MODULE, {open,BootFile,Reason}}
+ end;
+ {error, Reason} ->
+ {error, ?MODULE, {open,ScriptFile,Reason}}
+ end.
+
+path_flag(Flags) ->
+ case {member(local,Flags), member(otp_build, Flags)} of
+ {true, _} -> local;
+ {_, true} -> otp_build;
+ {_, _} -> true
+ end.
+
+get_variables(Flags) ->
+ case get_flag(variables, Flags) of
+ {variables, Variables} when is_list(Variables) ->
+ valid_variables(Variables);
+ _ ->
+ []
+ end.
+
+valid_variables([{Var,Path}|Variables]) when is_list(Var), is_list(Path) ->
+ [{Var,rm_tlsl(Path)}|valid_variables(Variables)];
+valid_variables([{Var,Path}|Variables]) when is_atom(Var), is_list(Path) ->
+ [{to_list(Var),rm_tlsl(Path)}|valid_variables(Variables)];
+valid_variables([_|Variables]) ->
+ valid_variables(Variables);
+valid_variables(_) ->
+ [].
+
+rm_tlsl(P) -> rm_tlsl1(reverse(P)).
+rm_tlsl1([$/|P]) -> rm_tlsl1(P);
+rm_tlsl1(P) -> reverse(P).
+
+%%______________________________________________________________________
+%% Start all applications.
+%% Do not start applications that are included applications !
+
+create_start_appls(Appls) ->
+ Included = append(map(fun({_,A}) ->
+ A#application.includes
+ end, Appls)),
+ create_start_appls(Appls, Included).
+
+create_start_appls([{_,A}|T], Incl) ->
+ App = A#application.name,
+ case lists:member(App, Incl) of
+ false when A#application.type == none ->
+ create_start_appls(T, Incl);
+ false when A#application.type == load ->
+ create_start_appls(T, Incl);
+ false ->
+ [{apply, {application, start_boot, [App,A#application.type]}} |
+ create_start_appls(T, Incl)];
+ _ ->
+ create_start_appls(T, Incl)
+ end;
+create_start_appls([], _) ->
+ [].
+
+%%______________________________________________________________________
+%% Load all applications.
+
+create_load_appls([{{kernel,_},_}|T]) -> %Already added !!
+ create_load_appls(T);
+create_load_appls([{_,A}|T]) when A#application.type == none ->
+ create_load_appls(T);
+create_load_appls([{_,A}|T]) ->
+ [{apply, {application, load, [pack_app(A)]}} |
+ create_load_appls(T)];
+create_load_appls([]) ->
+ [{progress, applications_loaded}].
+
+%%______________________________________________________________________
+%% The final part of the script.
+
+script_end() ->
+ [{apply, {c, erlangrc, []}},
+ {progress, started}].
+
+%%-----------------------------------------------------------------
+%% Function: sort_appls(Appls) -> {ok, Appls'} | throw({error, Error})
+%% Types: Appls = {{Name, Vsn}, #application}]
+%% Purpose: Sort applications according to dependencies among
+%% applications. If order doesn't matter, use the same
+%% order as in the original list.
+%% Alg. written by Ulf Wiger 970917 ([email protected])
+%% Mod. by mbj
+%%-----------------------------------------------------------------
+sort_appls(Appls) -> {ok, sort_appls(Appls, [], [], [])}.
+
+sort_appls([{N, A}|T], Missing, Circular, Visited) ->
+ {Name,_Vsn} = N,
+ {Uses, T1, NotFnd1} = find_all(Name, A#application.uses, T, Visited, [], []),
+ {Incs, T2, NotFnd2} = find_all(Name, lists:reverse(A#application.includes),
+ T1, Visited, [], []),
+ Missing1 = NotFnd1 ++ NotFnd2 ++ Missing,
+ case Uses ++ Incs of
+ [] ->
+ %% No more app that must be started before this one is
+ %% found; they are all already taken care of (and present
+ %% in Visited list)
+ [{N, A}|sort_appls(T, Missing1, Circular, [N|Visited])];
+ L ->
+ %% The apps in L must be started before the app.
+ %% Check if we have already taken care of some app in L,
+ %% in that case we have a circular dependency.
+ NewCircular = [N1 || {N1, _} <- L, N2 <- Visited, N1 == N2],
+ Circular1 = case NewCircular of
+ [] -> Circular;
+ _ -> [N | NewCircular] ++ Circular
+ end,
+ %% L must be started before N, try again, with all apps
+ %% in L added before N.
+ Apps = del_apps(NewCircular, L ++ [{N, A}|T2]),
+ sort_appls(Apps, Missing1, Circular1, [N|Visited])
+ end;
+sort_appls([], [], [], _) ->
+ [];
+sort_appls([], Missing, [], _) ->
+ %% this has already been checked before, but as we have the info...
+ throw({error, {undefined_applications, make_set(Missing)}});
+sort_appls([], [], Circular, _) ->
+ throw({error, {circular_dependencies, make_set(Circular)}});
+sort_appls([], Missing, Circular, _) ->
+ throw({error, {apps, [{circular_dependencies, make_set(Circular)},
+ {undefined_applications, make_set(Missing)}]}}).
+
+find_all(CheckingApp, [Name|T], L, Visited, Found, NotFound) ->
+ case find_app(Name, L) of
+ {value, App} ->
+ {_A,R} = App,
+ %% It is OK to have a dependecy like
+ %% X includes Y, Y uses X.
+ case lists:member(CheckingApp, R#application.includes) of
+ true ->
+ case lists:keymember(Name, 1, Visited) of
+ true ->
+ find_all(CheckingApp, T, L, Visited, Found, NotFound);
+ false ->
+ find_all(CheckingApp, T, L, Visited, Found, [Name|NotFound])
+ end;
+ false ->
+ find_all(CheckingApp, T, L -- [App], Visited, [App|Found], NotFound)
+ end;
+ false ->
+ case lists:keymember(Name, 1, Visited) of
+ true ->
+ find_all(CheckingApp, T, L, Visited, Found, NotFound);
+ false ->
+ find_all(CheckingApp, T, L, Visited, Found, [Name|NotFound])
+ end
+ end;
+find_all(_CheckingApp, [], L, _Visited, Found, NotFound) ->
+ {Found, L, NotFound}.
+
+find_app(Name, [{{Name,Vsn}, Application}|_]) ->
+ {value, {{Name,Vsn},Application}};
+find_app(Name, [_|T]) ->
+ find_app(Name, T);
+find_app(_Name, []) ->
+ false.
+
+del_apps([Name|T], L) ->
+ del_apps(T, lists:keydelete(Name, 1, L));
+del_apps([], L) ->
+ L.
+
+
+%%______________________________________________________________________
+%% Create the load path used in the generated script.
+%% If PathFlag is true a script intended to be used as a complete
+%% system (e.g. in an embbeded system), i.e. all applications are
+%% located under $ROOT/lib.
+%% Otherwise all paths are set according to dir per application.
+
+%% Create the complete path.
+create_path(Appls, PathFlag, Variables) ->
+ make_set(map(fun({{Name,Vsn},App}) ->
+ cr_path(Name, Vsn, App, PathFlag, Variables)
+ end,
+ Appls)).
+
+%% Create the path to a specific application.
+%% (The otp_build flag is only used for OTP internal system make)
+cr_path(Name, Vsn, _, true, []) ->
+ filename:join(["$ROOT", "lib", to_list(Name) ++ "-" ++ Vsn, "ebin"]);
+cr_path(Name, Vsn, App, true, Variables) ->
+ Dir = App#application.dir,
+ N = to_list(Name),
+ Tail = [N ++ "-" ++ Vsn, "ebin"],
+ case variable_dir(Dir, N, Vsn, Variables) of
+ {ok, VarDir} ->
+ filename:join([VarDir] ++ Tail);
+ _ ->
+ filename:join(["$ROOT", "lib"] ++ Tail)
+ end;
+cr_path(Name, _, _, otp_build, _) ->
+ filename:join(["$ROOT", "lib", to_list(Name), "ebin"]);
+cr_path(_, _, App, _, _) ->
+ filename:absname(App#application.dir).
+
+variable_dir(Dir, Name, Vsn, [{Var,Path}|Variables]) ->
+ case lists:prefix(Path,Dir) of
+ true ->
+ D0 = strip_prefix(Path, Dir),
+ case strip_name_ebin(D0, Name, Vsn) of
+ {ok, D} ->
+ {ok, filename:join(["\$" ++ Var] ++ D)};
+ _ ->
+ %% We know at least that we are located
+ %% under the variable dir.
+ {ok, filename:join(["\$" ++ Var] ++ D0)}
+ end;
+ _ ->
+ variable_dir(Dir, Name, Vsn, Variables)
+ end;
+variable_dir(_Dir, _, _, []) ->
+ false.
+
+strip_prefix(Path, Dir) ->
+ L = length(filename:split(Path)),
+ lists:nthtail(L, filename:split(Dir)).
+
+strip_name_ebin(Dir, Name, Vsn) ->
+ FullName = Name ++ "-" ++ Vsn,
+ case reverse(Dir) of
+ ["ebin",Name|D] -> {ok, reverse(D)};
+ ["ebin",FullName|D] -> {ok, reverse(D)};
+ _ -> false
+ end.
+
+%% Create the path to the kernel and stdlib applications.
+create_mandatory_path(Appls, PathFlag, Variables) ->
+ Dirs = [kernel, stdlib],
+ make_set(map(fun({{Name,Vsn}, A}) ->
+ case lists:member(Name, Dirs) of
+ true ->
+ cr_path(Name, Vsn, A, PathFlag, Variables);
+ _ ->
+ ""
+ end
+ end,
+ Appls)).
+
+%%______________________________________________________________________
+%% Load all modules, except those in Mandatory_modules.
+
+load_appl_mods([{{Name,Vsn},A}|Appls], Mand, PathFlag, Variables) ->
+ Mods = map(fun({Mod,_}) -> Mod;
+ (Mod) -> Mod
+ end,
+ A#application.modules),
+ load_commands(filter(fun(Mod) -> not member(Mod, Mand) end, Mods),
+ cr_path(Name, Vsn, A, PathFlag, Variables)) ++
+ load_appl_mods(Appls, Mand, PathFlag, Variables);
+% [{path, [cr_path(Name, Vsn, A, PathFlag, Variables)]},
+% {primLoad, filter(fun(Mod) -> not member(Mod, Mand) end, Mods)} |
+% load_appl_mods(Appls, Mand, PathFlag, Variables)];
+load_appl_mods([], _, _, _) ->
+ [{progress, modules_loaded}].
+
+load_commands(Mods, Path) ->
+ SplitMods = lists:foldl(
+ fun({Parts,M}, [{Last, Acc}|Rest]) ->
+ [_|Tail] = lists:reverse(Parts),
+ case lists:reverse(Tail) of
+ Subs when Subs == Last ->
+ [{Last,[M|Acc]}|Rest];
+ Subs ->
+ [{Subs, [M]}|[{Last,Acc}|Rest]]
+ end
+ end, [{[],[]}],
+ lists:sort([{packages:split(M),M} || M <- Mods])),
+ lists:foldl(
+ fun({Subs,Ms}, Cmds) ->
+ [{path, [filename:join([Path | Subs])]},
+ {primLoad,lists:sort(Ms)} | Cmds]
+ end, [], SplitMods).
+
+
+%%______________________________________________________________________
+%% Pack an application to an application term.
+
+pack_app(#application{name=Name,vsn=V,id=Id,description=D,modules=M,
+ uses=App,includes=Incs,regs=Regs,mod=Mod,start_phases=SF,
+ env=Env,maxT=MaxT,maxP=MaxP}) ->
+ {application, Name,
+ [{description,D},
+ {vsn,V},
+ {id,Id},
+ {modules, M},
+ {registered, Regs},
+ {applications, App},
+ {included_applications, Incs},
+ {env, Env},
+ {start_phases, SF},
+ {maxT, MaxT},
+ {maxP, MaxP} |
+ behave(Mod)]}.
+
+behave([]) ->
+ [];
+behave(Mod) ->
+ [{mod, Mod}].
+
+%%______________________________________________________________________
+%% mandatory modules; this modules must be loaded before processes
+%% can be started. These are a collection of modules from the kernel
+%% and stdlib applications.
+%% Nowadays, error_handler dynamically loads almost every module.
+%% The error_handler self must still be there though.
+
+mandatory_modules() ->
+ %% Sorted
+ [error_handler].
+
+%%______________________________________________________________________
+%% This is the modules that are preloaded into the Erlang system.
+
+preloaded() ->
+ %% Sorted
+ [erl_prim_loader,erlang,init,otp_ring0,prim_file,prim_inet, prim_zip,zlib].
+
+%%______________________________________________________________________
+%% Kernel processes; processes that are specially treated by the init
+%% process. If a kernel process terminates the whole system terminates.
+%% kernel_processes() -> [{Name, Mod, Func, Args}]
+%% where Args is a term or a fun taking the list of applications as arg.
+
+kernel_processes() ->
+ [{heart, heart, start, []},
+ {error_logger, error_logger, start_link, []},
+ {application_controller, application_controller, start,
+ fun(Appls) ->
+ [{_,App}] = filter(fun({{kernel,_},_App}) -> true;
+ (_) -> false
+ end,
+ Appls),
+ [pack_app(App)]
+ end}
+ ].
+
+%%______________________________________________________________________
+%% Create the kernel processes.
+
+create_kernel_procs(Appls) ->
+ map(fun({Name,Mod,Func,Args}) when is_function(Args) ->
+ {kernelProcess, Name, {Mod, Func, Args(Appls)}};
+ ({Name,Mod,Func,Args}) ->
+ {kernelProcess, Name, {Mod, Func, Args}}
+ end,
+ kernel_processes()) ++
+ [{progress, init_kernel_started}].
+
+%%______________________________________________________________________
+%% Make a tar file of the release.
+%% The tar file contains:
+%% lib/App-Vsn/ebin
+%% /priv
+%% [/src]
+%% [/include]
+%% [/doc]
+%% [/examples]
+%% [/...]
+%% Variable1.tar.gz
+%% ...
+%% VariableN.tar.gz
+%% releases/RelName.rel
+%% RelVsn/start.boot
+%% relup
+%% sys.config
+%% erts-EVsn[/bin]
+%%
+%% The VariableN.tar.gz files can also be stored as own files not
+%% included in the main tar file or they can be omitted using
+%% the var_tar option.
+
+mk_tar(RelName, Release, Appls, Flags, Path1) ->
+ TarName = case get_outdir(Flags) of
+ "" ->
+ RelName ++ ".tar.gz";
+ OutDir ->
+ filename:join(OutDir, filename:basename(RelName))
+ ++ ".tar.gz"
+ end,
+ Tar = open_main_tar(TarName),
+ case catch mk_tar(Tar, RelName, Release, Appls, Flags, Path1) of
+ {error,Error} ->
+ del_tar(Tar, TarName),
+ {error,?MODULE,Error};
+ {'EXIT',Reason} ->
+ del_tar(Tar, TarName),
+ {error,?MODULE,Reason};
+ _ ->
+ close_tar(Tar),
+ ok
+ end.
+
+open_main_tar(TarName) ->
+ case catch open_tar(TarName) of
+ {error, Error} ->
+ throw({error,?MODULE,Error});
+ Tar ->
+ Tar
+ end.
+
+mk_tar(Tar, RelName, Release, Appls, Flags, Path1) ->
+ Variables = get_variables(Flags),
+ add_applications(Appls, Tar, Variables, Flags, false),
+ add_variable_tars(Variables, Appls, Tar, Flags),
+ add_system_files(Tar, RelName, Release, Path1),
+ add_erts_bin(Tar, Release, Flags).
+
+add_applications(Appls, Tar, Variables, Flags, Var) ->
+ Res = foldl(fun({{Name,Vsn},App}, Errs) ->
+ case catch add_appl(to_list(Name), Vsn, App,
+ Tar, Variables, Flags, Var) of
+ ok ->
+ Errs;
+ {error, What} ->
+ [{error_add_appl, {Name,What}}|Errs]
+ end
+ end, [], Appls),
+ case Res of
+ [] ->
+ ok;
+ Errors ->
+ throw({error, Errors})
+ end.
+
+%%______________________________________________________________________
+%% Create a tar file for each Variable directory.
+%% Deletes the temporary tar file.
+
+add_variable_tars([Variable|Variables], Appls, Tar, Flags) ->
+ add_variable_tar(Variable, Appls, Tar, Flags),
+ add_variable_tars(Variables, Appls, Tar, Flags);
+add_variable_tars([], _, _, _) ->
+ ok.
+
+add_variable_tar({Variable,P}, Appls, Tar, Flags) ->
+ case var_tar_flag(Flags) of
+ omit ->
+ ok;
+ Flag ->
+ TarName = Variable ++ ".tar.gz",
+ VarTar = open_tar(TarName),
+ case catch add_applications(Appls, VarTar, [{Variable,P}],
+ Flags, Variable) of
+ ok when Flag == include ->
+ close_tar(VarTar),
+ add_to_tar(Tar, TarName, TarName),
+ del_file(TarName);
+ ok when Flag == ownfile ->
+ close_tar(VarTar),
+ ok;
+ Error ->
+ del_tar(VarTar, TarName),
+ throw(Error)
+ end
+ end.
+
+var_tar_flag(Flags) ->
+ case get_flag(var_tar, Flags) of
+ {var_tar, Flag} ->
+ case member(Flag, [include, ownfile, omit]) of
+ true -> Flag;
+ _ -> include
+ end;
+ _ ->
+ include
+ end.
+
+%%______________________________________________________________________
+%% Add all "other" files to Dir/releases/Svsn
+%% add_system_files(Tar,Name,release#,Flags) ->
+%% ok | throw({error,Error})
+
+add_system_files(Tar, RelName, Release, Path1) ->
+ SVsn = Release#release.vsn,
+ RelName0 = filename:basename(RelName),
+
+ add_to_tar(Tar, RelName ++ ".rel",
+ filename:join("releases", RelName0 ++ ".rel")),
+
+ %% OTP-6226 Look for the system files not only in cwd
+ %% --
+ %% (well, actually the boot file was looked for in the same
+ %% directory as RelName, which is not necessarily the same as cwd)
+ %% --
+ %% but also in the path specfied as an option to systools:make_tar
+ %% (but make sure to search the RelName directory and cwd first)
+ Path = case filename:dirname(RelName) of
+ "." ->
+ ["."|Path1];
+ RelDir ->
+ [RelDir, "."|Path1]
+ end,
+
+ ToDir = filename:join("releases", SVsn),
+ case lookup_file(RelName0 ++ ".boot", Path) of
+ false ->
+ throw({error, {tar_error,{add, RelName0++".boot",enoent}}});
+ Boot ->
+ add_to_tar(Tar, Boot, filename:join(ToDir, "start.boot"))
+ end,
+
+ case lookup_file("relup", Path) of
+ false ->
+ ignore;
+ Relup ->
+ add_to_tar(Tar, Relup, filename:join(ToDir, "relup"))
+ end,
+
+ case lookup_file("sys.config", Path) of
+ false ->
+ ignore;
+ Sys ->
+ add_to_tar(Tar, Sys, filename:join(ToDir, "sys.config"))
+ end,
+
+ ok.
+
+lookup_file(Name, [Dir|Path]) ->
+ File = filename:join(Dir, Name),
+ case filelib:is_file(File) of
+ true ->
+ File;
+ false ->
+ lookup_file(Name, Path)
+ end;
+lookup_file(_Name, []) ->
+ false.
+
+%%______________________________________________________________________
+%% Add either a application located under a variable dir or all other
+%% applications to a tar file.
+%% add_appl(Name,Vsn,application#,Tar,Variables,Flags,Var) ->
+%% ok | {error,Error}
+
+add_appl(Name, Vsn, App, Tar, Variables, Flags, Var) ->
+ AppDir = App#application.dir,
+ case add_to(AppDir,Name,Vsn,Variables,Var) of
+ false ->
+ ok;
+ {ok, ToDir} ->
+ ADir = appDir(AppDir),
+ add_priv(ADir, ToDir, Tar),
+ case get_flag(dirs,Flags) of
+ {dirs,Dirs} ->
+ add_dirs(ADir, Dirs, ToDir, Tar);
+ _ ->
+ ok
+ end,
+ BinDir = filename:join(ToDir, "ebin"),
+ add_to_tar(Tar,
+ filename:join(AppDir, Name ++ ".app"),
+ filename:join(BinDir, Name ++ ".app")),
+ add_modules(map(fun({Mod,_}) -> to_list(Mod);
+ (Mod) -> to_list(Mod)
+ end,
+ App#application.modules),
+ Tar,
+ AppDir,
+ BinDir,
+ objfile_extension(machine(Flags)))
+ end.
+
+%%______________________________________________________________________
+%% If an application directory contains a Variable (in AppDir) the
+%% application will be placed in the tar file (if it is this Variable
+%% we corrently is actually storing).
+
+add_to(AppDir,Name,Vsn,Variables,Variable) ->
+ case var_dir(AppDir,Name,Vsn,Variables) of
+ {ok, Variable, RestPath} ->
+ {ok, filename:join(RestPath ++ [Name ++ "-" ++ Vsn])};
+ {ok, _, _} ->
+ false;
+ _ when Variable == false ->
+ {ok, filename:join("lib", Name ++ "-" ++ Vsn)};
+ _ ->
+ false
+ end.
+
+var_dir(Dir, Name, Vsn, [{Var,Path}|Variables]) ->
+ case lists:prefix(Path,Dir) of
+ true ->
+ D0 = strip_prefix(Path, Dir),
+ case strip_name_ebin(D0, Name, Vsn) of
+ {ok, D} ->
+ {ok, Var, D};
+ _ ->
+ false
+ end;
+ _ ->
+ var_dir(Dir, Name, Vsn, Variables)
+ end;
+var_dir(_Dir, _, _, []) ->
+ false.
+
+appDir(AppDir) ->
+ case reverse(filename:split(AppDir)) of
+ ["ebin"|Dir] -> filename:join(reverse(Dir));
+ _ -> AppDir
+ end.
+
+add_modules(Modules, Tar, AppDir, ToDir, Ext) ->
+ foreach(fun(Mod) ->
+ add_to_tar(Tar,
+ filename:join(AppDir, Mod ++ Ext),
+ filename:join(ToDir, Mod ++ Ext))
+ end, Modules).
+
+%%
+%% Add own specified directories to include in the release.
+%% If not found, skip it.
+%%
+add_dirs(AppDir, Dirs, ToDir, Tar) ->
+ foreach(fun(Dir) -> catch add_dir(AppDir, to_list(Dir), ToDir, Tar) end,
+ Dirs).
+
+add_dir(TopDir, Dir, ToDir, Tar) ->
+ FromD = filename:join(TopDir, Dir),
+ case dirp(FromD) of
+ true ->
+ add_to_tar(Tar, FromD, filename:join(ToDir, Dir));
+ _ ->
+ ok
+ end.
+
+%%
+%% Add the priv dir if it exists.
+
+add_priv(ADir, ToDir, Tar) ->
+ Priv = filename:join(ADir, "priv"),
+ case dirp(Priv) of
+ true ->
+ add_to_tar(Tar, Priv, filename:join(ToDir, "priv"));
+ _ ->
+ ok
+ end.
+
+add_erts_bin(Tar, Release, Flags) ->
+ case get_flag(erts,Flags) of
+ {erts,ErtsDir} ->
+ EVsn = Release#release.erts_vsn,
+ FromDir = filename:join([to_list(ErtsDir),
+ "erts-" ++ EVsn, "bin"]),
+ dirp(FromDir),
+ ToDir = filename:join("erts-" ++ EVsn, "bin"),
+ add_to_tar(Tar, FromDir, ToDir);
+ _ ->
+ ok
+ end.
+
+%%______________________________________________________________________
+%% Tar functions.
+
+open_tar(TarName) ->
+ case erl_tar:open(TarName, [write, compressed]) of
+ {ok, Tar} ->
+ Tar;
+ {error, Error} ->
+ throw({error,{tar_error, {open, TarName, Error}}})
+ end.
+
+close_tar(Tar) ->
+ erl_tar:close(Tar).
+
+del_tar(Tar, TarName) ->
+ close_tar(Tar),
+ del_file(TarName).
+
+add_to_tar(Tar, FromFile, ToFile) ->
+ case erl_tar:add(Tar, FromFile, ToFile, [compressed, dereference]) of
+ ok -> ok;
+ {error, Error} ->
+ throw({error, {tar_error, {add, FromFile, Error}}})
+ end.
+
+%%______________________________________________________________________
+%%______________________________________________________________________
+%% utilities!
+
+make_set([]) -> [];
+make_set([""|T]) -> % Ignore empty items.
+ make_set(T);
+make_set([H|T]) ->
+ [H | [ Y || Y<- make_set(T),
+ Y =/= H]].
+
+to_list(A) when is_atom(A) -> atom_to_list(A);
+to_list(L) -> L.
+
+mk_path(Path0) ->
+ Path1 = map(fun(Dir) when is_atom(Dir) -> atom_to_list(Dir);
+ (Dir) -> Dir
+ end, Path0),
+ systools_lib:get_path(Path1).
+
+%% duplicates([Tuple]) -> List of pairs where
+%% element(1, T1) == element(1, T2) and where T1 and T2 are
+%% taken from [Tuple]
+
+duplicates(X) -> duplicates(keysort(1,X), []).
+
+duplicates([H1,H2|T], L) ->
+ case {element(1,H1),element(1,H2)} of
+ {X,X} -> duplicates([H2|T],[{H1,H2}|L]);
+ _ -> duplicates([H2|T],L)
+ end;
+duplicates(_, L) -> L.
+
+%% read_file(File, Path) -> {ok, Term, FullName} | {error, Error}
+%% read a file and check the syntax, i.e. that it contains a correct
+%% Erlang term.
+
+read_file(File, Path) ->
+ case file:path_open(Path, File, [read]) of
+ {ok, Stream, FullName} ->
+ Return = case systools_lib:read_term_from_stream(Stream, File) of
+ {ok, Term} ->
+ {ok, Term, FullName};
+ Other ->
+ Other
+ end,
+ file:close(Stream),
+ Return;
+ _Other ->
+ {error, {not_found, File}}
+ end.
+
+del_file(File) -> file:delete(File).
+
+dirp(Dir) ->
+ case file:read_file_info(Dir) of
+ {ok, FileInfo} -> FileInfo#file_info.type == directory;
+ _ -> false
+ end.
+
+%% Create the include path. Assumptions about the code path is done
+%% and an include directory is added.
+%% Add the official include dir for each found application first in
+%% path !!
+%% If .../ebin exists in a path an .../include directory is assumed to
+%% exist at the same level. If .../ebin is not existing the .../include
+%% directory is assumed anyhow.
+%% Local includes are added for each application later on.
+
+create_include_path(Appls, Path) ->
+ FoundAppDirs = map(fun({_,A}) -> A#application.dir end, Appls),
+ map(fun(Dir) ->
+ case reverse(filename:split(Dir)) of
+ ["ebin"|D] ->
+ filename:join(reverse(D) ++ ["include"]);
+ _ ->
+ filename:join(Dir, "include")
+ end
+ end,
+ FoundAppDirs ++ no_dupl(Path, FoundAppDirs)).
+
+no_dupl([Dir|Path], FoundAppDirs) ->
+ case member(Dir, FoundAppDirs) of
+ true ->
+ no_dupl(Path, FoundAppDirs);
+ _ ->
+ [Dir|no_dupl(Path, FoundAppDirs)]
+ end;
+no_dupl([], _) ->
+ [].
+
+is_app_type(permanent) -> true;
+is_app_type(transient) -> true;
+is_app_type(temporary) -> true;
+is_app_type(none) -> true;
+is_app_type(load) -> true;
+is_app_type(_) -> false.
+
+% check if a term is a string.
+
+string_p([H|T]) when is_integer(H), H >= $ , H < 255 ->
+ string_p(T);
+string_p([$\n|T]) -> string_p(T);
+string_p([$\r|T]) -> string_p(T);
+string_p([$\t|T]) -> string_p(T);
+string_p([$\v|T]) -> string_p(T);
+string_p([$\b|T]) -> string_p(T);
+string_p([$\f|T]) -> string_p(T);
+string_p([$\e|T]) -> string_p(T);
+string_p([]) -> true;
+string_p(_) -> false.
+
+% check if a term is a list of two tuples with the first
+% element as an atom.
+
+t_list_p([{A,_}|T]) when is_atom(A) -> t_list_p(T);
+t_list_p([]) -> true;
+t_list_p(_) -> false.
+
+% check if a term is a list of atoms or two-tuples with the first
+% element as an atom.
+
+mod_list_p([{A,_}|T]) when is_atom(A) -> mod_list_p(T);
+mod_list_p([A|T]) when is_atom(A) -> mod_list_p(T);
+mod_list_p([]) -> true;
+mod_list_p(_) -> false.
+
+% check if a term is a list of atoms.
+
+a_list_p([A|T]) when is_atom(A) -> a_list_p(T);
+a_list_p([]) -> true;
+a_list_p(_) -> false.
+
+%% Get a key-value tuple flag from a list.
+
+get_flag(F,[{F,D}|_]) -> {F,D};
+get_flag(F,[_|Fs]) -> get_flag(F,Fs);
+get_flag(_,_) -> false.
+
+%% Check Options for make_script
+check_args_script(Args) ->
+ cas(Args,
+ {undef, undef, undef, undef, undef, undef, undef, undef, []}).
+
+cas([], {_Path,_Sil,_Loc,_Test,_Var,_Mach,_Xref,_XrefApps, X}) ->
+ X;
+%%% path ---------------------------------------------------------------
+cas([{path, P} | Args], {Path, Sil, Loc, Test, Var, Mach,
+ Xref, XrefApps, X}) when is_list(P) ->
+ case check_path(P) of
+ ok ->
+ cas(Args, {P, Sil, Loc, Test, Var, Mach, Xref, XrefApps,X});
+ error ->
+ cas(Args, {Path, Sil, Loc, Test, Var, Mach, Xref, XrefApps,
+ X++[{path,P}]})
+ end;
+%%% silent -------------------------------------------------------------
+cas([silent | Args], {Path, _Sil, Loc, Test, Var, Mach,
+ Xref, XrefApps, X}) ->
+ cas(Args, {Path, silent, Loc, Test, Var, Mach, Xref, XrefApps, X});
+%%% local --------------------------------------------------------------
+cas([local | Args], {Path, Sil, _Loc, Test, Var, Mach,
+ Xref, XrefApps, X}) ->
+ cas(Args, {Path, Sil, local, Test, Var, Mach, Xref, XrefApps, X});
+%%% no_module_tests ----------------------------------------------------
+cas([no_module_tests | Args], {Path, Sil, Loc, _Test, Var, Mach,
+ Xref, XrefApps, X}) ->
+ cas(Args,
+ {Path, Sil, Loc, no_module_tests, Var, Mach, Xref, XrefApps,X});
+%%% variables ----------------------------------------------------------
+cas([{variables, V} | Args], {Path, Sil, Loc, Test, Var, Mach,
+ Xref, XrefApps, X}) when is_list(V) ->
+ case check_vars(V) of
+ ok ->
+ cas(Args,
+ {Path, Sil, Loc, Test, V, Mach, Xref, XrefApps, X});
+ error ->
+ cas(Args, {Path, Sil, Loc, Test, Var, Mach, Xref, XrefApps,
+ X++[{variables, V}]})
+ end;
+%%% machine ------------------------------------------------------------
+cas([{machine, M} | Args], {Path, Sil, Loc, Test, Var, Mach,
+ Xref, XrefApps, X}) when is_atom(M) ->
+ cas(Args, {Path, Sil, Loc, Test, Var, Mach, Xref, XrefApps, X});
+%%% exref --------------------------------------------------------------
+cas([exref | Args], {Path, Sil, Loc, Test, Var, Mach,
+ _Xref, XrefApps, X}) ->
+ cas(Args, {Path, Sil, Loc, Test, Var, Mach, exref, XrefApps, X});
+%%% exref Apps ---------------------------------------------------------
+cas([{exref, Apps} | Args], {Path, Sil, Loc, Test, Var, Mach,
+ Xref, XrefApps, X}) when is_list(Apps) ->
+ case check_apps(Apps) of
+ ok ->
+ cas(Args, {Path, Sil, Loc, Test, Var, Mach,
+ Xref, Apps, X});
+ error ->
+ cas(Args, {Path, Sil, Loc, Test, Var, Mach,
+ Xref, XrefApps, X++[{exref, Apps}]})
+ end;
+%%% outdir Dir ---------------------------------------------------------
+cas([{outdir, Dir} | Args], {Path, Sil, Loc, Test, Var, Mach,
+ Xref, XrefApps, X}) when is_list(Dir) ->
+ cas(Args, {Path, Sil, Loc, Test, Var, Mach, Xref, XrefApps, X});
+%%% otp_build (secret, not documented) ---------------------------------
+cas([otp_build | Args], {Path, Sil, Loc, Test, Var, Mach,
+ Xref, XrefApps, X}) ->
+ cas(Args, {Path, Sil, Loc, Test, Var, Mach, Xref, XrefApps, X});
+%%% ERROR --------------------------------------------------------------
+cas([Y | Args], {Path, Sil, Loc, Test, Var, Mach, Xref, XrefApps, X}) ->
+ cas(Args, {Path, Sil, Loc, Test, Var, Mach, Xref, XrefApps,X++[Y]}).
+
+
+
+%% Check Options for make_tar
+check_args_tar(Args) ->
+ cat(Args, {undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, []}).
+
+cat([], {_Path,_Sil,_Dirs,_Erts,_Test,_Var,_VarTar,_Mach,_Xref,_XrefApps, X}) ->
+ X;
+%%% path ---------------------------------------------------------------
+cat([{path, P} | Args], {Path, Sil, Dirs, Erts, Test,
+ Var, VarTar, Mach, Xref, XrefApps, X}) when is_list(P) ->
+ case check_path(P) of
+ ok ->
+ cat(Args, {P, Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X});
+ error ->
+ cat(Args, {Path, Sil, Dirs, Erts, Test,
+ Var, VarTar, Mach, Xref, XrefApps, X++[{path,P}]})
+ end;
+%%% silent -------------------------------------------------------------
+cat([silent | Args], {Path, _Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X}) ->
+ cat(Args, {Path, silent, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X});
+%%% dirs ---------------------------------------------------------------
+cat([{dirs, D} | Args], {Path, Sil, Dirs, Erts, Test,
+ Var, VarTar, Mach, Xref, XrefApps, X}) ->
+ case check_dirs(D) of
+ ok ->
+ cat(Args, {Path, Sil, D, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X});
+ error ->
+ cat(Args, {Path, Sil, Dirs, Erts, Test,
+ Var, VarTar, Mach, Xref, XrefApps, X++[{dirs, D}]})
+ end;
+%%% erts ---------------------------------------------------------------
+cat([{erts, E} | Args], {Path, Sil, Dirs, _Erts, Test,
+ Var, VarTar, Mach, Xref, XrefApps, X}) when is_list(E)->
+ cat(Args, {Path, Sil, Dirs, E, Test, Var, VarTar, Mach, Xref, XrefApps, X});
+%%% no_module_tests ----------------------------------------------------
+cat([no_module_tests | Args], {Path, Sil, Dirs, Erts, _Test, Var, VarTar, Mach, Xref, XrefApps, X}) ->
+ cat(Args, {Path, Sil, Dirs, Erts, no_module_tests, Var, VarTar, Mach,
+ Xref, XrefApps, X});
+%%% variables ----------------------------------------------------------
+cat([{variables, V} | Args], {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X}) when is_list(V) ->
+ case check_vars(V) of
+ ok ->
+ cat(Args, {Path, Sil, Dirs, Erts, Test, V, VarTar, Mach, Xref, XrefApps, X});
+ error ->
+ cat(Args, {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach,
+ Xref, XrefApps, X++[{variables, V}]})
+ end;
+%%% var_tar ------------------------------------------------------------
+cat([{var_tar, VT} | Args], {Path, Sil, Dirs, Erts, Test,
+ Var, _VarTar, Mach, Xref, XrefApps, X}) when VT == include ->
+ cat(Args, {Path, Sil, Dirs, Erts, Test, Var, include, Mach, Xref, XrefApps, X});
+cat([{var_tar, VT} | Args], {Path, Sil, Dirs, Erts, Test,
+ Var, _VarTar, Mach, Xref, XrefApps, X}) when VT == ownfile ->
+ cat(Args, {Path, Sil, Dirs, Erts, Test, Var, ownfile, Mach, Xref, XrefApps, X});
+cat([{var_tar, VT} | Args], {Path, Sil, Dirs, Erts, Test,
+ Var, _VarTar, Mach, Xref, XrefApps, X}) when VT == omit ->
+ cat(Args, {Path, Sil, Dirs, Erts, Test, Var, omit, Mach, Xref, XrefApps, X});
+%%% machine ------------------------------------------------------------
+cat([{machine, M} | Args], {Path, Sil, Dirs, Erts, Test,
+ Var, VarTar, Mach, Xref, XrefApps, X}) when is_atom(M) ->
+ cat(Args, {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X});
+%%% exref --------------------------------------------------------------
+cat([exref | Args], {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, _Xref, XrefApps, X}) ->
+ cat(Args, {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, exref, XrefApps, X});
+%%% exref Apps ---------------------------------------------------------
+cat([{exref, Apps} | Args], {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X}) when is_list(Apps) ->
+ case check_apps(Apps) of
+ ok ->
+ cat(Args, {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach,
+ Xref, Apps, X});
+ error ->
+ cat(Args, {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach,
+ Xref, XrefApps, X++[{exref, Apps}]})
+ end;
+%%% outdir Dir ---------------------------------------------------------
+cat([{outdir, Dir} | Args], {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X}) when is_list(Dir) ->
+ cat(Args, {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach,
+ Xref, XrefApps, X});
+%%% otp_build (secret, not documented) ---------------------------------
+cat([otp_build | Args], {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X}) ->
+ cat(Args, {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X});
+%%% ERROR --------------------------------------------------------------
+cat([Y | Args], {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X}) ->
+ cat(Args, {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X++[Y]}).
+
+check_path([]) ->
+ ok;
+check_path([H|T]) when is_list(H) ->
+ check_path(T);
+check_path([_H|_T]) ->
+ error.
+
+check_dirs([]) ->
+ ok;
+check_dirs([H|T]) when is_atom(H) ->
+ check_dirs(T);
+check_dirs([_H|_T]) ->
+ error.
+
+check_vars([]) ->
+ ok;
+check_vars([{Name, Dir} | T]) ->
+ if
+ is_atom(Name), is_list(Dir) ->
+ check_vars(T);
+ is_list(Name), is_list(Dir) ->
+ check_vars(T);
+ true ->
+ error
+ end;
+check_vars(_) ->
+ error.
+
+check_apps([]) ->
+ ok;
+check_apps([H|T]) when is_atom(H) ->
+ check_apps(T);
+check_apps(_) ->
+ error.
+
+
+%% Format error
+
+format_error(badly_formatted_release) ->
+ io_lib:format("Syntax error in the release file~n",[]);
+format_error({illegal_name, Name}) ->
+ io_lib:format("Illegal name (~p) in the release file~n",[Name]);
+format_error({illegal_form, Form}) ->
+ io_lib:format("Illegal tag in the release file: ~p~n",[Form]);
+format_error({missing_parameter,Par}) ->
+ io_lib:format("Missing parameter (~p) in the release file~n",[Par]);
+format_error({illegal_applications,Names}) ->
+ io_lib:format("Illegal applications in the release file: ~p~n",
+ [Names]);
+format_error({missing_mandatory_app,Names}) ->
+ io_lib:format("Mandatory applications (~p) must be specified in the release file~n",
+ [Names]);
+format_error({duplicate_register,Dups}) ->
+ io_lib:format("Duplicated register names: ~n~s",
+ [map(fun({{Reg,App1,_,_},{Reg,App2,_,_}}) ->
+ io_lib:format("\t~p registered in ~p and ~p~n",
+ [Reg,App1,App2])
+ end, Dups)]);
+format_error({undefined_applications,Apps}) ->
+ io_lib:format("Undefined applications: ~p~n",[Apps]);
+format_error({duplicate_modules,Dups}) ->
+ io_lib:format("Duplicated modules: ~n~s",
+ [map(fun({{Mod,_,App1,_,_},{Mod,_,App2,_,_}}) ->
+ io_lib:format("\t~p specified in ~p and ~p~n",
+ [Mod,App1,App2])
+ end, Dups)]);
+format_error({included_and_used, Dups}) ->
+ io_lib:format("Applications both used and included: ~p~n",[Dups]);
+format_error({duplicate_include, Dups}) ->
+ io_lib:format("Duplicated application included: ~n~s",
+ [map(fun({{Name,App1,_,_},{Name,App2,_,_}}) ->
+ io_lib:format("\t~p included in ~p and ~p~n",
+ [Name,App1,App2])
+ end, Dups)]);
+format_error({modules,ModErrs}) ->
+ format_errors(ModErrs);
+format_error({circular_dependencies,Apps}) ->
+ io_lib:format("Circular dependencies among applications: ~p~n",[Apps]);
+format_error({not_found,File}) ->
+ io_lib:format("File not found: ~p~n",[File]);
+format_error({parse,File,{Line,Mod,What}}) ->
+ Str = Mod:format_error(What),
+ io_lib:format("~s:~p: ~s\n",[File, Line, Str]);
+format_error({read,File}) ->
+ io_lib:format("Cannot read ~p~n",[File]);
+format_error({open,File,Error}) ->
+ io_lib:format("Cannot open ~p - ~s~n",
+ [File,file:format_error(Error)]);
+format_error({tar_error,What}) ->
+ form_tar_err(What);
+format_error(ListOfErrors) when is_list(ListOfErrors) ->
+ format_errors(ListOfErrors);
+format_error(E) -> io_lib:format("~p~n",[E]).
+
+format_errors(ListOfErrors) ->
+ map(fun({error,E}) -> form_err(E);
+ (E) -> form_err(E)
+ end, ListOfErrors).
+
+form_err({bad_application_name,{Name,Found}}) ->
+ io_lib:format("~p: Mismatched application id: ~p~n",[Name,Found]);
+form_err({error_reading, {Name, What}}) ->
+ io_lib:format("~p: ~s~n",[Name,form_reading(What)]);
+form_err({module_not_found,App,Mod}) ->
+ io_lib:format("~p: Module (~p) not found~n",[App,Mod]);
+form_err({{vsn_diff,File},{Mod,Vsn,App,_,_}}) ->
+ io_lib:format("~p: Module (~p) version (~p) differs in file ~p~n",
+ [App,Mod,Vsn,File]);
+form_err({error_add_appl, {Name, {tar_error, What}}}) ->
+ io_lib:format("~p: ~s~n",[Name,form_tar_err(What)]);
+form_err(E) ->
+ io_lib:format("~p~n",[E]).
+
+form_reading({not_found,File}) ->
+ io_lib:format("File not found: ~p~n",[File]);
+form_reading({application_vsn, {Name,Vsn}}) ->
+ io_lib:format("Application ~s with version ~p not found~n",[Name, Vsn]);
+form_reading({parse,File,{Line,Mod,What}}) ->
+ Str = Mod:format_error(What),
+ io_lib:format("~s:~p: ~s\n",[File, Line, Str]);
+form_reading({read,File}) ->
+ io_lib:format("Cannot read ~p~n",[File]);
+form_reading({{bad_param, P},_}) ->
+ io_lib:format("Bad parameter in .app file: ~p~n",[P]);
+form_reading({{missing_param,P},_}) ->
+ io_lib:format("Missing parameter in .app file: ~p~n",[P]);
+form_reading({badly_formatted_application,_}) ->
+ io_lib:format("Syntax error in .app file~n",[]);
+form_reading({override_include,Apps}) ->
+ io_lib:format("Tried to include not (in .app file) specified applications: ~p~n",
+ [Apps]);
+form_reading({no_valid_version, {{_, SVsn}, {_, File, FVsn}}}) ->
+ io_lib:format("No valid version (~p) of .app file found. Found file ~p with version ~p~n",
+ [SVsn, File, FVsn]);
+form_reading({parse_error, {File, Line, Error}}) ->
+ io_lib:format("Parse error in file: ~p. Line: ~p Error: ~p; ~n", [File, Line, Error]);
+form_reading(W) ->
+ io_lib:format("~p~n",[W]).
+
+form_tar_err({open, File, Error}) ->
+ io_lib:format("Cannot open tar file ~s - ~p~n",
+ [File, erl_tar:format_error(Error)]);
+form_tar_err({add, File, Error}) ->
+ io_lib:format("Cannot add file ~s to tar file - ~s~n",
+ [File, erl_tar:format_error(Error)]).
+
+%% Format warning
+
+format_warning(Warnings) ->
+ map(fun({warning,W}) -> form_warn(W) end, Warnings).
+
+form_warn({source_not_found,{Mod,_,App,_,_}}) ->
+ io_lib:format("*WARNING* ~p: Source code not found: ~p.erl~n",
+ [App,Mod]);
+form_warn({{parse_error, File},{_,_,App,_,_}}) ->
+ io_lib:format("*WARNING* ~p: Parse error: ~p~n",
+ [App,File]);
+form_warn({obj_out_of_date,{Mod,_,App,_,_}}) ->
+ io_lib:format("*WARNING* ~p: Object code (~p) out of date~n",[App,Mod]);
+form_warn({exref_undef, Undef}) ->
+ F = fun({M,F,A}) ->
+ io_lib:format("*WARNING* Undefined function ~p:~p/~p~n",
+ [M,F,A])
+ end,
+ map(F, Undef);
+form_warn(What) ->
+ io_lib:format("*WARNING* ~p~n", [What]).