1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1996-2020. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20-module(systools_make).
21
22%% Purpose : Create start script. RelName.rel --> RelName.{script,boot}.
23%%           and create a tar file of a release (RelName.tar.gz)
24
25-export([make_script/1, make_script/2, make_script/3,
26	 make_tar/1, make_tar/2]).
27
28-export([format_error/1, format_warning/1]).
29
30-export([read_release/2, get_release/2, get_release/3, pack_app/1]).
31
32-export([read_application/4]).
33
34-export([make_hybrid_boot/4]).
35-export([preloaded/0]). % Exported just for testing
36
37-import(lists, [filter/2, keysort/2, keysearch/3, map/2, reverse/1,
38		append/1, foldl/3,  member/2, foreach/2]).
39
40-include("systools.hrl").
41
42-include_lib("kernel/include/file.hrl").
43
44-define(XREF_SERVER, systools_make).
45
46-compile({inline,[{badarg,2}]}).
47
48-ifdef(USE_ESOCK).
49-define(ESOCK_MODS, [prim_net,prim_socket,socket_registry]).
50-else.
51-define(ESOCK_MODS, []).
52-endif.
53
54
55%%-----------------------------------------------------------------
56%% Create a boot script from a release file.
57%% Options is a list of {path, Path} | silent | local
58%%         | warnings_as_errors
59%% where path sets the search path, silent supresses error message
60%% printing on console, local generates a script with references
61%% to the directories there the applications are found,
62%% and warnings_as_errors treats warnings as errors.
63%%
64%% New options: {path,Path} can contain wildcards
65%%              src_tests
66%%              {variables,[{Name,AbsString}]}
67%%              exref | {exref, [AppName]}
68%%              no_warn_sasl
69%%-----------------------------------------------------------------
70
71make_script(RelName) when is_list(RelName) ->
72    make_script(RelName, []);
73make_script(RelName) ->
74    badarg(RelName,[RelName]).
75
76make_script(RelName, Flags) when is_list(RelName), is_list(Flags) ->
77    ScriptName = get_script_name(RelName, Flags),
78    case get_outdir(Flags) of
79	"" ->
80	    make_script(RelName, ScriptName, Flags);
81	OutDir ->
82	    %% To maintain backwards compatibility for make_script/3,
83	    %% the boot script file name is constructed here, before
84	    %% checking the validity of OutDir
85	    %% (is done in check_args_script/1)
86	    Output = filename:join(OutDir, filename:basename(ScriptName)),
87	    make_script(RelName, Output, Flags)
88    end.
89
90make_script(RelName, Output, Flags) when is_list(RelName),
91					 is_list(Output),
92					 is_list(Flags) ->
93    case check_args_script(Flags) of
94	[] ->
95	    Path0 = get_path(Flags),
96	    Path1 = mk_path(Path0), % expand wildcards etc.
97	    Path  = make_set(Path1 ++ code:get_path()),
98	    ModTestP = {member(src_tests, Flags),xref_p(Flags)},
99	    case get_release(RelName, Path, ModTestP) of
100		{ok, Release, Appls, Warnings0} ->
101		    Warnings = wsasl(Flags, Warnings0),
102		    case systools_lib:werror(Flags, Warnings) of
103			true ->
104                            Warnings1 = [W || {warning,W}<-Warnings],
105			    return({error,?MODULE,
106                                    {warnings_treated_as_errors,Warnings1}},
107                                   Warnings,
108                                   Flags);
109			false ->
110			    case generate_script(Output,Release,Appls,Flags) of
111				ok ->
112				    return(ok,Warnings,Flags);
113				Error ->
114				    return(Error,Warnings,Flags)
115			    end
116		    end;
117		Error ->
118		    return(Error,[],Flags)
119	    end;
120	ErrorVars ->
121	    badarg(ErrorVars, [RelName, Flags])
122    end;
123
124make_script(RelName, _Output, Flags) when is_list(Flags) ->
125    badarg(RelName,[RelName, Flags]);
126make_script(RelName, _Output, Flags) ->
127    badarg(Flags,[RelName, Flags]).
128
129wsasl(Options, Warnings) ->
130    case lists:member(no_warn_sasl,Options) of
131	true -> lists:delete({warning,missing_sasl},Warnings);
132	false -> Warnings
133    end.
134
135badarg(BadArg, Args) ->
136    erlang:error({badarg,BadArg}, Args).
137
138get_script_name(RelName, Flags) ->
139    case get_flag(script_name,Flags) of
140	{script_name,ScriptName} when is_list(ScriptName) -> ScriptName;
141	_    -> RelName
142    end.
143
144get_path(Flags) ->
145    case get_flag(path,Flags) of
146	{path,Path} when is_list(Path) -> Path;
147	_                              -> []
148    end.
149
150get_outdir(Flags) ->
151    case get_flag(outdir,Flags) of
152	{outdir,OutDir} when is_list(OutDir) ->
153	    OutDir;
154	_ -> % false | {outdir, Badarg}
155	    ""
156    end.
157
158return(ok,Warnings,Flags) ->
159    case member(silent,Flags) of
160	true ->
161            {ok,?MODULE,Warnings};
162	_ ->
163            io:format("~ts",[format_warning(Warnings)]),
164            ok
165    end;
166return({error,Mod,Error},_,Flags) ->
167    case member(silent,Flags) of
168	true ->
169	    {error,Mod,Error};
170	_ ->
171	    io:format("~ts",[Mod:format_error(Error)]),
172	    error
173    end.
174
175
176%%-----------------------------------------------------------------
177%% Make hybrid boot file for upgrading emulator. The resulting boot
178%% file is a combination of the two input files, where kernel, stdlib
179%% and sasl versions are taken from the second file (the boot file of
180%% the new release), and all other application versions from the first
181%% file (the boot file of the old release).
182%%
183%% The most important thing that can fail here is that the input boot
184%% files do not contain all three base applications - kernel, stdlib
185%% and sasl.
186%%
187%% TmpVsn = string(),
188%% Returns {ok,Boot} | {error,Reason}
189%% Boot1 = Boot2 = Boot = binary()
190%% Reason = {app_not_found,App} | {app_not_replaced,App}
191%% App = stdlib | sasl
192make_hybrid_boot(TmpVsn, Boot1, Boot2, Args) ->
193    catch do_make_hybrid_boot(TmpVsn, Boot1, Boot2, Args).
194do_make_hybrid_boot(TmpVsn, OldBoot, NewBoot, Args) ->
195    {script,{_RelName1,_RelVsn1},OldScript} = binary_to_term(OldBoot),
196    {script,{NewRelName,_RelVsn2},NewScript} = binary_to_term(NewBoot),
197
198    %% Everyting upto kernel_load_completed must come from the new script
199    Fun1 = fun({progress,kernel_load_completed}) -> false;
200              (_) -> true
201           end,
202    {_OldKernelLoad,OldRest1} = lists:splitwith(Fun1,OldScript),
203    {NewKernelLoad,NewRest1} = lists:splitwith(Fun1,NewScript),
204
205    Fun2 = fun({progress,modules_loaded}) -> false;
206              (_) -> true
207           end,
208    {OldModLoad,OldRest2} = lists:splitwith(Fun2,OldRest1),
209    {NewModLoad,NewRest2} = lists:splitwith(Fun2,NewRest1),
210
211    Fun3 = fun({kernelProcess,_,_}) -> false;
212              (_) -> true
213           end,
214    {OldPaths,OldRest3} = lists:splitwith(Fun3,OldRest2),
215    {NewPaths,NewRest3} = lists:splitwith(Fun3,NewRest2),
216
217    Fun4 = fun({progress,init_kernel_started}) -> false;
218              (_) -> true
219           end,
220    {_OldKernelProcs,OldApps} = lists:splitwith(Fun4,OldRest3),
221    {NewKernelProcs,NewApps} = lists:splitwith(Fun4,NewRest3),
222
223    %% Then comes all module load, which for each app consist of:
224    %% {path,[AppPath]},
225    %% {primLoad,ModuleList}
226    %% Replace kernel, stdlib and sasl here
227    MatchPaths = get_regexp_path(),
228    ModLoad = replace_module_load(OldModLoad,NewModLoad,MatchPaths),
229    Paths = replace_paths(OldPaths,NewPaths,MatchPaths),
230
231    {Stdlib,Sasl} = get_apps(NewApps,undefined,undefined),
232    Apps0 = replace_apps(OldApps,Stdlib,Sasl),
233    Apps = add_apply_upgrade(Apps0,Args),
234
235    Script = NewKernelLoad++ModLoad++Paths++NewKernelProcs++Apps,
236    Boot = term_to_binary({script,{NewRelName,TmpVsn},Script}),
237    {ok,Boot}.
238
239%% For each app, compile a regexp that can be used for finding its path
240get_regexp_path() ->
241    {ok,KernelMP} = re:compile("kernel-[0-9\.]+",[unicode]),
242    {ok,StdlibMP} = re:compile("stdlib-[0-9\.]+",[unicode]),
243    {ok,SaslMP} = re:compile("sasl-[0-9\.]+",[unicode]),
244    [KernelMP,StdlibMP,SaslMP].
245
246replace_module_load(Old,New,[MP|MatchPaths]) ->
247    replace_module_load(do_replace_module_load(Old,New,MP),New,MatchPaths);
248replace_module_load(Script,_,[]) ->
249    Script.
250
251do_replace_module_load([{path,[OldAppPath]},{primLoad,OldMods}|OldRest],New,MP) ->
252    case re:run(OldAppPath,MP,[{capture,none}]) of
253        nomatch ->
254            [{path,[OldAppPath]},{primLoad,OldMods}|
255             do_replace_module_load(OldRest,New,MP)];
256        match ->
257            get_module_load(New,MP) ++ OldRest
258    end;
259do_replace_module_load([Other|Rest],New,MP) ->
260    [Other|do_replace_module_load(Rest,New,MP)];
261do_replace_module_load([],_,_) ->
262    [].
263
264get_module_load([{path,[AppPath]},{primLoad,Mods}|Rest],MP) ->
265    case re:run(AppPath,MP,[{capture,none}]) of
266        nomatch ->
267            get_module_load(Rest,MP);
268        match ->
269            [{path,[AppPath]},{primLoad,Mods}]
270    end;
271get_module_load([_|Rest],MP) ->
272    get_module_load(Rest,MP);
273get_module_load([],_) ->
274    [].
275
276replace_paths([{path,OldPaths}|Old],New,MatchPaths) ->
277    {path,NewPath} = lists:keyfind(path,1,New),
278    [{path,do_replace_paths(OldPaths,NewPath,MatchPaths)}|Old];
279replace_paths([Other|Old],New,MatchPaths) ->
280    [Other|replace_paths(Old,New,MatchPaths)].
281
282do_replace_paths(Old,New,[MP|MatchPaths]) ->
283    do_replace_paths(do_replace_paths1(Old,New,MP),New,MatchPaths);
284do_replace_paths(Paths,_,[]) ->
285    Paths.
286
287do_replace_paths1([P|Ps],New,MP) ->
288    case re:run(P,MP,[{capture,none}]) of
289        nomatch ->
290            [P|do_replace_paths1(Ps,New,MP)];
291        match ->
292            get_path(New,MP) ++ Ps
293    end;
294do_replace_paths1([],_,_) ->
295    [].
296
297get_path([P|Ps],MP) ->
298    case re:run(P,MP,[{capture,none}]) of
299        nomatch ->
300            get_path(Ps,MP);
301        match ->
302            [P]
303    end;
304get_path([],_) ->
305    [].
306
307
308%% Return the entries for loading stdlib and sasl
309get_apps([{apply,{application,load,[{application,stdlib,_}]}}=Stdlib|Script],
310	 _,Sasl) ->
311    get_apps(Script,Stdlib,Sasl);
312get_apps([{apply,{application,load,[{application,sasl,_}]}}=Sasl|_Script],
313	 Stdlib,_) ->
314    {Stdlib,Sasl};
315get_apps([_|Script],Stdlib,Sasl) ->
316    get_apps(Script,Stdlib,Sasl);
317get_apps([],undefined,_) ->
318    throw({error,{app_not_found,stdlib}});
319get_apps([],_,undefined) ->
320    throw({error,{app_not_found,sasl}}).
321
322%% Replace the entries for loading the stdlib and sasl
323replace_apps([{apply,{application,load,[{application,stdlib,_}]}}|Script],
324	     Stdlib,Sasl) ->
325    [Stdlib|replace_apps(Script,undefined,Sasl)];
326replace_apps([{apply,{application,load,[{application,sasl,_}]}}|Script],
327	     _Stdlib,Sasl) ->
328    [Sasl|Script];
329replace_apps([Stuff|Script],Stdlib,Sasl) ->
330    [Stuff|replace_apps(Script,Stdlib,Sasl)];
331replace_apps([],undefined,_) ->
332    throw({error,{app_not_replaced,sasl}});
333replace_apps([],_,_) ->
334    throw({error,{app_not_replaced,stdlib}}).
335
336%% Finally add an apply of release_handler:new_emulator_upgrade - which will
337%% complete the execution of the upgrade script (relup).
338add_apply_upgrade(Script,Args) ->
339    [{progress, started} | RevScript] = lists:reverse(Script),
340    lists:reverse([{progress,started},
341		   {apply,{release_handler,new_emulator_upgrade,Args}} |
342		   RevScript]).
343
344%%-----------------------------------------------------------------
345%% Create a release package from a release file.
346%% Options is a list of {path, Path} | silent |
347%%    {dirs, [src,include,examples,..]} | {erts, ErtsDir} where path
348%% sets the search path, silent supresses error message printing,
349%% dirs includes the specified directories (per application) in the
350%% release package and erts specifies that the erts-Vsn/bin directory
351%% should be included in the release package and there it can be found.
352%%
353%% New options: {path,Path} can contain wildcards
354%%              src_tests
355%%              exref | {exref, [AppName]}
356%%              {variables,[{Name,AbsString}]}
357%%              {var_tar, include | ownfile | omit}
358%%              no_warn_sasl
359%%              warnings_as_errors
360%%
361%% The tar file contains:
362%%         lib/App-Vsn/ebin
363%%                    /priv
364%%                   [/src]
365%%                   [/include]
366%%                   [/doc]
367%%                   [/examples]
368%%                   [/...]
369%%         Variable1.tar.gz
370%%         ...
371%%         VariableN.tar.gz
372%%         releases/RelName.rel
373%%                  RelVsn/start.boot
374%%                         relup
375%%                         sys.config
376%%                         sys.config.src
377%%         erts-EVsn[/bin]
378%%-----------------------------------------------------------------
379
380make_tar(RelName) when is_list(RelName) ->
381    make_tar(RelName, []);
382make_tar(RelName) ->
383    badarg(RelName,[RelName]).
384
385make_tar(RelName, Flags) when is_list(RelName), is_list(Flags) ->
386    case check_args_tar(Flags) of
387	[] ->
388	    Path0 = get_path(Flags),
389	    Path1 = mk_path(Path0),
390	    Path  = make_set(Path1 ++ code:get_path()),
391	    ModTestP = {member(src_tests, Flags),xref_p(Flags)},
392	    case get_release(RelName, Path, ModTestP) of
393		{ok, Release, Appls, Warnings0} ->
394		    Warnings = wsasl(Flags, Warnings0),
395		    case systools_lib:werror(Flags, Warnings) of
396			true ->
397                            Warnings1 = [W || {warning,W}<-Warnings],
398			    return({error,?MODULE,
399                                    {warnings_treated_as_errors,Warnings1}},
400                                   Warnings,
401                                   Flags);
402			false ->
403                            case catch mk_tar(RelName, Release, Appls, Flags, Path1) of
404                                ok ->
405                                    return(ok,Warnings,Flags);
406                                Error ->
407                                    return(Error,Warnings,Flags)
408                            end
409                    end;
410		Error ->
411		    return(Error,[],Flags)
412	    end;
413	ErrorVars ->
414	    badarg(ErrorVars, [RelName, Flags])
415    end;
416make_tar(RelName, Flags) when is_list(Flags) ->
417    badarg(RelName,[RelName, Flags]);
418make_tar(RelName, Flags) ->
419    badarg(Flags,[RelName, Flags]).
420
421%%______________________________________________________________________
422%% get_release(File, Path) ->
423%% get_release(File, Path, ModTestP) ->
424%%     {ok, #release, [{{Name,Vsn},#application}], Warnings} | {error, What}
425
426get_release(File, Path) ->
427    get_release(File, Path, {false,false}).
428
429get_release(File, Path, ModTestP) ->
430    case catch get_release1(File, Path, ModTestP) of
431	{error, Error} ->
432	    {error, ?MODULE, Error};
433	{'EXIT', Why} ->
434	    {error, ?MODULE, {'EXIT',Why}};
435	Answer ->
436	    Answer
437    end.
438
439get_release1(File, Path, ModTestP) ->
440    {ok, Release, Warnings1} = read_release(File, Path),
441    {ok, Appls0} = collect_applications(Release, Path),
442    {ok, Appls1} = check_applications(Appls0),
443    {ok, Appls2} = sort_used_and_incl_appls(Appls1, Release), % OTP-4121, OTP-9984
444    {ok, Warnings2} = check_modules(Appls2, Path, ModTestP),
445    {ok, Appls} = sort_appls(Appls2),
446    {ok, Release, Appls, Warnings1 ++ Warnings2}.
447
448%%______________________________________________________________________
449%% read_release(File, Path) -> {ok, #release} | throw({error, What})
450
451read_release(File, Path) ->
452    case read_file(File ++ ".rel", ["."|Path]) of
453	{ok, Release, _FullName} ->
454	    check_rel(Release);
455	{error,Error} ->
456	    throw({error,?MODULE,Error})
457    end.
458
459check_rel(Release) ->
460    case catch check_rel1(Release) of
461	{ok, {Name,Vsn,Evsn,Appl,Incl}, Ws} ->
462	    {ok, #release{name=Name, vsn=Vsn,
463			  erts_vsn=Evsn,
464			  applications=Appl,
465			  incl_apps=Incl},
466	    Ws};
467	{error, Error} ->
468	    throw({error,?MODULE,Error});
469	Error ->
470	    throw({error,?MODULE,Error})
471    end.
472
473check_rel1({release,{Name,Vsn},{erts,EVsn},Appl}) when is_list(Appl) ->
474    Name = check_name(Name),
475    Vsn = check_vsn(Vsn),
476    EVsn = check_evsn(EVsn),
477    {{Appls,Incls},Ws} = check_appl(Appl),
478    {ok, {Name,Vsn,EVsn,Appls,Incls},Ws};
479check_rel1(_) ->
480    {error, badly_formatted_release}.
481
482check_name(Name) ->
483    case string_p(Name) of
484	true ->
485	    Name;
486	_ ->
487	    throw({error,{illegal_name, Name}})
488    end.
489
490check_vsn(Vsn) ->
491    case string_p(Vsn) of
492	true ->
493	    Vsn;
494	_ ->
495	    throw({error,{illegal_form, Vsn}})
496    end.
497
498check_evsn(Vsn) ->
499    case string_p(Vsn) of
500	true ->
501	    Vsn;
502	_ ->
503	    throw({error,{illegal_form, {erts,Vsn}}})
504    end.
505
506check_appl(Appl) ->
507    case filter(fun({App,Vsn}) when is_atom(App) ->
508			not string_p(Vsn);
509		   ({App,Vsn,Incl}) when is_atom(App), is_list(Incl) ->
510			case {string_p(Vsn), a_list_p(Incl)} of
511			    {true, true} -> false;
512			    _            -> true
513			end;
514		   ({App,Vsn,Type}) when is_atom(App), is_atom(Type) ->
515			case {string_p(Vsn), is_app_type(Type)} of
516			    {true, true} -> false;
517			    _            -> true
518			end;
519		   ({App,Vsn,Type,Incl}) when is_atom(App),
520					      is_atom(Type),
521					      is_list(Incl) ->
522			case {string_p(Vsn),is_app_type(Type),a_list_p(Incl)} of
523			    {true, true, true} -> false;
524			    _                  -> true
525			end;
526		   (_) ->
527			true
528		end,
529		Appl) of
530	[] ->
531	    {ApplsNoIncls,Incls} = split_app_incl(Appl),
532	    {ok,Ws} = mandatory_applications(ApplsNoIncls,undefined,
533					     undefined,undefined),
534	    {{ApplsNoIncls,Incls},Ws};
535	Illegal ->
536	    throw({error, {illegal_applications,Illegal}})
537    end.
538
539mandatory_applications([{kernel,_,Type}|Apps],undefined,Stdlib,Sasl) ->
540    mandatory_applications(Apps,Type,Stdlib,Sasl);
541mandatory_applications([{stdlib,_,Type}|Apps],Kernel,undefined,Sasl) ->
542    mandatory_applications(Apps,Kernel,Type,Sasl);
543mandatory_applications([{sasl,_,Type}|Apps],Kernel,Stdlib,undefined) ->
544    mandatory_applications(Apps,Kernel,Stdlib,Type);
545mandatory_applications([_|Apps],Kernel,Stdlib,Sasl) ->
546    mandatory_applications(Apps,Kernel,Stdlib,Sasl);
547mandatory_applications([],Type,_,_) when Type=/=permanent ->
548    error_mandatory_application(kernel,Type);
549mandatory_applications([],_,Type,_) when Type=/=permanent ->
550    error_mandatory_application(stdlib,Type);
551mandatory_applications([],_,_,undefined) ->
552    {ok, [{warning,missing_sasl}]};
553mandatory_applications([],_,_,_) ->
554    {ok,[]}.
555
556error_mandatory_application(App,undefined) ->
557    throw({error, {missing_mandatory_app, App}});
558error_mandatory_application(App,Type) ->
559    throw({error, {mandatory_app, App, Type}}).
560
561split_app_incl(Appl) -> split_app_incl(Appl, [], []).
562
563split_app_incl([{App,Vsn}|Appls], Apps, Incls) ->
564    split_app_incl(Appls, [{App,Vsn,permanent}|Apps], Incls);
565split_app_incl([{App,Vsn,Incl}|Appls], Apps,Incls) when is_list(Incl) ->
566    split_app_incl(Appls, [{App,Vsn,permanent}|Apps], [{App,Incl}|Incls]);
567split_app_incl([{App,Vsn,Type}|Appls], Apps, Incls) ->
568    split_app_incl(Appls, [{App,Vsn,Type}|Apps], Incls);
569split_app_incl([{App,Vsn,Type,Incl}|Appls], Apps, Incls) when is_list(Incl) ->
570    split_app_incl(Appls, [{App,Vsn,Type}|Apps], [{App,Incl}|Incls]);
571split_app_incl([], Apps, Incls) ->
572    {reverse(Apps),reverse(Incls)}.
573
574%%______________________________________________________________________
575%% collect_applications(#release, Path) ->
576%%    {ok,[{{Name,Vsn},#application}]} |
577%%    throw({error, What})
578%% Read all the application files specified in the release descriptor
579
580collect_applications(Release, Path) ->
581    Appls = Release#release.applications,
582    Incls = Release#release.incl_apps,
583    X = foldl(fun({Name,Vsn,Type}, {Ok, Errs}) ->
584		      case read_application(to_list(Name), Vsn, Path, Incls) of
585			  {ok, A} ->
586			      case {A#application.name,A#application.vsn} of
587				 {Name,Vsn} ->
588				     {[{{Name,Vsn}, A#application{type=Type}} | Ok],
589				      Errs};
590				 E ->
591				     {Ok, [{bad_application_name, {Name, E}} | Errs]}
592			     end;
593			  {error, What} ->
594			      {Ok, [{error_reading, {Name, What}} | Errs]}
595		      end
596	      end, {[],[]}, Appls),
597    case X of
598	{A, []} ->
599	    {ok, reverse(A)};
600	{_, Errs} ->
601	    throw({error, Errs})
602    end.
603
604
605%%______________________________________________________________________
606%% read_application(Name, Vsn, Path, Incls) -> {ok, #release} | {error, What}
607
608read_application(Name, Vsn, Path, Incls) ->
609    read_application(Name, Vsn, Path, Incls, false, no_fault).
610
611read_application(Name, Vsn, [Dir|Path], Incls, Found, FirstError) ->
612    case read_file(Name ++ ".app", [Dir]) of
613	{ok, Term, FullName} ->
614	    case parse_application(Term, FullName, Vsn, Incls) of
615		{error, {no_valid_version, {Vsn, OtherVsn}}} when FirstError == no_fault ->
616		    NFE = {no_valid_version, {{"should be", Vsn},
617					       {"found file", filename:join(Dir, Name++".app"),
618						OtherVsn}}},
619		    read_application(Name, Vsn, Path, Incls, true, NFE);
620		{error, {no_valid_version, {Vsn, _OtherVsn}}} ->
621			    read_application(Name, Vsn, Path, Incls, true, FirstError);
622		Res ->
623		    Res
624	    end;
625	{error, {parse, _File, {Line, _Mod, Err}}} when FirstError == no_fault ->
626	    read_application(Name, Vsn, Path, Incls, Found,
627			     {parse_error, {filename:join(Dir, Name++".app"), Line, Err}});
628	{error, {parse, _File, _Err}} ->
629	    read_application(Name, Vsn, Path, Incls, Found, FirstError);
630	{error, _Err} -> %% Not found
631	    read_application(Name, Vsn, Path, Incls, Found, FirstError)
632    end;
633read_application(Name, Vsn, [], _, true, no_fault) ->
634    {error, {application_vsn, {Name,Vsn}}};
635read_application(_Name, _Vsn, [], _, true, FirstError) ->
636    {error, FirstError};
637read_application(Name, _, [], _, _, no_fault) ->
638    {error, {not_found, Name ++ ".app"}};
639read_application(_Name, _, [], _, _, FirstError) ->
640    {error, FirstError}.
641
642parse_application({application, Name, Dict}, File, Vsn, Incls)
643  when is_atom(Name),
644       is_list(Dict) ->
645    Items = [vsn,id,description,modules,registered,
646	     applications,included_applications,mod,start_phases,env,maxT,maxP],
647    case catch get_items(Items, Dict) of
648	[Vsn,Id,Desc,Mods,Regs,Apps,Incs0,Mod,Phases,Env,MaxT,MaxP] ->
649	    case override_include(Name, Incs0, Incls) of
650		{ok, Incs} ->
651		    {ok, #application{name=Name,
652				      vsn=Vsn,
653				      id=Id,
654				      description=Desc,
655				      modules=Mods,
656				      uses=Apps,
657				      includes=Incs,
658				      regs=Regs,
659				      mod=Mod,
660				      start_phases=Phases,
661				      env=Env,
662				      maxT=MaxT,
663				      maxP=MaxP,
664				      dir=filename:dirname(File)}};
665		{error, IncApps} ->
666		    {error, {override_include, IncApps}}
667	    end;
668	[OtherVsn,_,_,_,_,_,_,_,_,_,_,_] ->
669	    {error, {no_valid_version, {Vsn, OtherVsn}}};
670	Err ->
671	    {error, {Err, {application, Name, Dict}}}
672    end;
673parse_application(Other, _, _, _) ->
674    {error, {badly_formatted_application, Other}}.
675
676%% Test if all included applications specifed in the .rel file
677%% exists in the {included_applications,Incs} specified in the
678%% .app file.
679override_include(Name, Incs, Incls) ->
680    case keysearch(Name, 1, Incls) of
681	{value, {Name, I}} ->
682	    case specified(I, Incs) of
683		[] ->
684		    {ok, I};
685		NotSpec ->
686		    {error, NotSpec}
687	    end;
688	_ ->
689	    {ok, Incs}
690    end.
691
692specified([App|Incls], Spec) ->
693    case member(App, Spec) of
694	true ->
695	    specified(Incls, Spec);
696	_ ->
697	    [App|specified(Incls, Spec)]
698    end;
699specified([], _) ->
700    [].
701
702get_items([H|T], Dict) ->
703    Item = check_item(keysearch(H, 1, Dict),H),
704    [Item|get_items(T, Dict)];
705get_items([], _Dict) ->
706    [].
707
708check_item({_,{mod,{M,A}}},_) when is_atom(M) ->
709    {M,A};
710check_item({_,{mod,[]}},_) -> % default mod is [], so accept as entry
711    [];
712check_item({_,{vsn,Vsn}},I) ->
713    case string_p(Vsn) of
714	true -> Vsn;
715	_ -> throw({bad_param, I})
716    end;
717check_item({_,{id,Id}},I) ->
718    case string_p(Id) of
719	true -> Id;
720	_ -> throw({bad_param, I})
721    end;
722check_item({_,{description,Desc}},I) ->
723    case string_p(Desc) of
724	true -> Desc;
725	_ -> throw({bad_param, I})
726    end;
727check_item({_,{applications,Apps}},I) ->
728    case a_list_p(Apps) of
729	true -> Apps;
730	_ -> throw({bad_param, I})
731    end;
732check_item({_,{included_applications,Apps}},I) ->
733    case a_list_p(Apps) of
734	true -> Apps;
735	_ -> throw({bad_param, I})
736    end;
737check_item({_,{registered,Regs}},I) ->
738    case a_list_p(Regs) of
739	true -> Regs;
740	_ -> throw({bad_param, I})
741    end;
742check_item({_,{modules,Mods}},I) ->
743    case a_list_p(Mods) of
744	true -> Mods;
745	_ -> throw({bad_param, I})
746    end;
747check_item({_,{start_phases,undefined}},_) -> % default start_phase is undefined,
748    undefined;                                % so accept as entry
749check_item({_,{start_phases,Phase}},I) ->
750    case t_list_p(Phase) of
751	true -> Phase;
752	_ -> throw({bad_param, I})
753    end;
754check_item({_,{env,Env}},I) ->
755    case t_list_p(Env) of
756	true -> Env;
757	_ -> throw({bad_param, I})
758    end;
759check_item({_,{maxT,MaxT}},I) ->
760    case MaxT of
761	MaxT when is_integer(MaxT), MaxT > 0 -> MaxT;
762	infinity -> infinity;
763	_ -> throw({bad_param, I})
764    end;
765check_item({_,{maxP,MaxP}},I) ->
766    case MaxP of
767	MaxP when is_integer(MaxP), MaxP > 0 -> MaxP;
768	infinity -> infinity;
769	_ -> throw({bad_param, I})
770    end;
771check_item(false, included_applications) -> % optional !
772    [];
773check_item(false, mod) -> % mod is optional !
774    [];
775check_item(false, env) -> % env is optional !
776    [];
777check_item(false, id) -> % id is optional !
778    [];
779check_item(false, start_phases) -> % start_phases is optional !
780    undefined;
781check_item(false, maxT) -> % maxT is optional !
782    infinity;
783check_item(false, maxP) -> % maxP is optional !
784    infinity;
785check_item(_, Item) ->
786    throw({missing_param, Item}).
787
788%%______________________________________________________________________
789%% check_applications([{{Name,Vsn},#application}]) ->
790%%    ok | throw({error, Error})
791%% check that all referenced applications exists and that no
792%% application register processes with the same name.
793%% Check that included_applications are not specified as used
794%% in another application.
795
796check_applications(Appls) ->
797    undef_appls(Appls),
798    dupl_regs(Appls),
799    %% Make a list Incs = [{Name,App,AppVsn,Dir}]
800    Incs = [{IncApp,App,Appv,A#application.dir} ||
801	       {{App,Appv},A} <- Appls,
802	       IncApp <- A#application.includes],
803    dupl_incls(Incs),
804    Res = add_top_apps_to_uses(Incs, Appls, []),
805    {ok, Res}.
806
807
808
809undef_appls(Appls) ->
810    case undefined_applications(Appls) of
811	[] ->
812	    ok;
813	L ->
814	    throw({error, {undefined_applications, make_set(L)}})
815    end.
816
817dupl_regs(Appls) ->
818    %% Make a list Regs = [{Name,App,AppVsn,Dir}]
819    Regs = [{Name,App,Appv,A#application.dir} ||
820	       {{App,Appv},A} <- Appls,
821	       Name <- A#application.regs],
822    case duplicates(Regs) of
823	[] ->
824	    ok;
825	Dups ->
826	    throw({error, {duplicate_register, Dups}})
827    end.
828
829
830dupl_incls(Incs) ->
831    case duplicates(Incs) of
832	[] ->
833	    ok;
834	Dups ->
835	    throw({error, {duplicate_include, Dups}})
836    end.
837
838
839
840%% If an application uses another application which is included in yet
841%% another application, e.g. X uses A, A is included in T; then the A
842%% application in the X applications uses-variable is changed to the T
843%% application's top application to ensure the start order.
844%% Exception: if both X and A have the same top, then it is not
845%% added to avoid circular dependencies.
846%%
847%% add_top_apps_to_uses( list of all included applications in
848%%                       the system,
849%%                       list of all applications in the system,
850%%                       temporary result)
851%% -> new list of all applications
852add_top_apps_to_uses(_InclApps, [], Res) ->
853    %% InclApps = [{IncApp, App, AppVsn, Dir}]
854    Res;
855add_top_apps_to_uses(InclApps, [{Name,Appl} | Appls], Res) ->
856    MyTop = find_top_app(Appl#application.name, InclApps),
857    F = fun(UsedApp, AccIn) when UsedApp == MyTop ->
858		%% UW980513 This is a special case: The included app
859		%% uses its own top app. We'll allow it, but must
860		%% remove the top app from the uses list.
861		AccIn -- [MyTop];
862	   (UsedApp, AccIn) ->
863		case lists:keysearch(UsedApp, 1, InclApps) of
864		    false ->
865			AccIn;
866		    {value, {_,DependApp,_,_}} ->
867			UsedAppTop = find_top_app(DependApp, InclApps),
868			case {lists:member(UsedAppTop, AccIn), MyTop} of
869			    {true, _} ->
870				%% the top app is already in the uses
871				%% list, remove UsedApp
872				AccIn -- [UsedApp];
873			    {_, UsedAppTop} ->
874				%% both are included in the same app
875				AccIn;
876			    _ ->
877				%% change the used app to the used app's
878				%% top application
879				AccIn1 = AccIn -- [UsedApp],
880				AccIn1 ++ [UsedAppTop]
881			end
882		end
883	end,
884
885    NewUses = foldl(F, Appl#application.uses, Appl#application.uses),
886    add_top_apps_to_uses(InclApps, Appls,
887			 Res++[{Name, Appl#application{uses = NewUses}}]).
888
889
890
891find_top_app(App, InclApps) ->
892    case lists:keysearch(App, 1, InclApps) of
893	false ->
894	    App;
895	{value, {_,TopApp,_,_}} ->
896	    find_top_app(TopApp, InclApps)
897    end.
898
899
900
901%%______________________________________________________________________
902%% undefined_applications([{{Name,Vsn},#application}]) ->
903%%   [Name] list of applications that were declared in
904%%   use declarations but are not contained in the release descriptor
905
906undefined_applications(Appls) ->
907    Uses = append(map(fun({_,A}) ->
908			      A#application.uses ++ A#application.includes
909		      end, Appls)),
910    Defined = map(fun({{X,_},_}) -> X end, Appls),
911    filter(fun(X) -> not member(X, Defined) end, Uses).
912
913%%______________________________________________________________________
914%% sort_used_and_incl_appls(Applications, Release) -> Applications
915%%   Applications = [{{Name,Vsn},#application}]
916%%   Release = #release{}
917%%
918%% OTP-4121, OTP-9984
919%% Check that used and included applications are given in the same
920%% order as in the release resource file (.rel). Otherwise load and
921%% start instructions in the boot script, and consequently release
922%% upgrade instructions in relup, may end up in the wrong order.
923
924sort_used_and_incl_appls(Applications, Release) when is_tuple(Release) ->
925    {ok,
926     sort_used_and_incl_appls(Applications, Release#release.applications)};
927
928sort_used_and_incl_appls([{Tuple,Appl}|Appls], OrderedAppls) ->
929    Incls2 =
930	case Appl#application.includes of
931	    Incls when length(Incls)>1 ->
932		sort_appl_list(Incls, OrderedAppls);
933	    Incls ->
934		Incls
935	end,
936    Uses2 =
937	case Appl#application.uses of
938	    Uses when length(Uses)>1 ->
939		sort_appl_list(Uses, OrderedAppls);
940	    Uses ->
941		Uses
942	end,
943    Appl2 = Appl#application{includes=Incls2, uses=Uses2},
944    [{Tuple,Appl2}|sort_used_and_incl_appls(Appls, OrderedAppls)];
945sort_used_and_incl_appls([], _OrderedAppls) ->
946    [].
947
948sort_appl_list(List, Order) ->
949    IndexedList = find_pos(List, Order),
950    SortedIndexedList = lists:keysort(1, IndexedList),
951    lists:map(fun({_Index,Name}) -> Name end, SortedIndexedList).
952
953find_pos([Name|Incs], OrderedAppls) ->
954    [find_pos(1, Name, OrderedAppls)|find_pos(Incs, OrderedAppls)];
955find_pos([], _OrderedAppls) ->
956    [].
957
958find_pos(N, Name, [{Name,_Vsn,_Type}|_OrderedAppls]) ->
959    {N, Name};
960find_pos(N, Name, [_OtherAppl|OrderedAppls]) ->
961    find_pos(N+1, Name, OrderedAppls).
962
963%%______________________________________________________________________
964%% check_modules(Appls, Path, TestP) ->
965%%  {ok, Warnings} | throw({error, What})
966%%   where Appls = [{App,Vsn}, #application}]
967%%   performs logical checking that we can find all the modules
968%%   etc.
969
970check_modules(Appls, Path, TestP) ->
971    %% first check that all the module names are unique
972    %% Make a list M1 = [{Mod,App,Dir}]
973    M1 = [{Mod,App,A#application.dir} ||
974	     {{App,_Appv},A} <- Appls,
975	     Mod <- A#application.modules],
976    case duplicates(M1) of
977	[] ->
978	    case check_mods(M1, Appls, Path, TestP) of
979		{error, Errors} ->
980		    throw({error, {modules, Errors}});
981		Return ->
982		    Return
983	    end;
984	Dups ->
985%	    io:format("** ERROR Duplicate modules: ~p\n", [Dups]),
986	    throw({error, {duplicate_modules, Dups}})
987    end.
988
989%%______________________________________________________________________
990%% Check that all modules exists.
991%% Use the module extension of the running machine as extension for
992%% the checked modules.
993
994check_mods(Modules, Appls, Path, {SrcTestP, XrefP}) ->
995    SrcTestRes = check_src(Modules, Appls, Path, SrcTestP),
996    XrefRes = check_xref(Appls, Path, XrefP),
997    Res = SrcTestRes ++ XrefRes,
998    case filter(fun({error, _}) -> true;
999		   (_)          -> false
1000		end,
1001		Res) of
1002	[] ->
1003	    {ok, filter(fun({warning, _}) -> true;
1004			   (_)            -> false
1005			end,
1006			Res)};
1007	Errors ->
1008	    {error, Errors}
1009    end.
1010
1011check_src(Modules, Appls, Path, true) ->
1012    Ext = code:objfile_extension(),
1013    IncPath = create_include_path(Appls, Path),
1014    append(map(fun(ModT) ->
1015		       {Mod,App,Dir} = ModT,
1016		       case check_mod(Mod,App,Dir,Ext,IncPath) of
1017			   ok ->
1018			       [];
1019			   {error, Error} ->
1020			       [{error,{Error, ModT}}];
1021			   {warning, Warn} ->
1022			       [{warning,{Warn,ModT}}]
1023		       end
1024	       end,
1025	       Modules));
1026check_src(_, _, _, _) ->
1027    [].
1028
1029check_xref(_Appls, _Path, false) ->
1030    [];
1031check_xref(Appls, Path, XrefP) ->
1032    AppDirsL = [{App,A#application.dir} || {{App,_Appv},A} <- Appls],
1033    AppDirs0 = sofs:relation(AppDirsL),
1034    AppDirs = case XrefP of
1035		  true ->
1036		      AppDirs0;
1037		  {true, Apps} ->
1038		      sofs:restriction(AppDirs0, sofs:set(Apps))
1039	      end,
1040    XrefArgs = [{xref_mode, modules}],
1041    case catch xref:start(?XREF_SERVER, XrefArgs) of
1042	{ok, _Pid} ->
1043	    ok;
1044	{error, {already_started, _Pid}} ->
1045	    xref:stop(?XREF_SERVER), %% Clear out any previous data
1046	    {ok,_} = xref:start(?XREF_SERVER, XrefArgs),
1047	    ok
1048    end,
1049    {ok, _} = xref:set_default(?XREF_SERVER, verbose, false),
1050    LibPath = case Path == code:get_path() of
1051		  true -> code_path; % often faster
1052		  false -> Path
1053	      end,
1054    ok = xref:set_library_path(?XREF_SERVER, LibPath),
1055    check_xref(sofs:to_external(AppDirs)).
1056
1057check_xref([{App,AppDir} | Appls]) ->
1058    case xref:add_application(?XREF_SERVER, AppDir, {name,App}) of
1059	{ok, _App} ->
1060	    check_xref(Appls);
1061	Error ->
1062	    xref:stop(?XREF_SERVER),
1063	    [{error, Error}]
1064    end;
1065check_xref([]) ->
1066    R = case xref:analyze(?XREF_SERVER, undefined_functions) of
1067	    {ok, []} ->
1068		[];
1069	    {ok, Undefined} ->
1070		%% This clause is a (temporary?) fix for hipe.
1071		adjust_for_hipe(Undefined);
1072	    Error ->
1073		[{error, Error}]
1074	end,
1075    xref:stop(?XREF_SERVER),
1076    R.
1077
1078adjust_for_hipe(Undef) ->
1079    case erlang:system_info(hipe_architecture) of
1080	undefined ->
1081	    U = lists:filter(fun ({hipe_bifs,_,_}) -> false;
1082				 ({hipe,_,_}) -> false;
1083				 (_) -> true
1084			     end, Undef),
1085	    if
1086		[] == U ->
1087		    [];
1088		true ->
1089		    [{warning, {exref_undef, U}}]
1090	    end;
1091	_Arch ->
1092	    %% Some BIFs are not always available on all versions of HiPE.
1093	    U = lists:filter(fun ({hipe_bifs,write_u64,2}) -> false;
1094				 (_) -> true
1095			     end, Undef),
1096	    [{warning, {exref_undef, U}}]
1097    end.
1098
1099%% Perform cross reference checks between all modules specified
1100%% in .app files.
1101%%
1102xref_p(Flags) ->
1103    case member(exref, Flags) of
1104	true ->
1105	    exists_xref(true);
1106	_ ->
1107	    case get_flag(exref, Flags) of
1108		{exref, Appls} when is_list(Appls) ->
1109		    case a_list_p(Appls) of
1110			true -> exists_xref({true, Appls});
1111			_    -> false
1112		    end;
1113		_ ->
1114		    false
1115	    end
1116    end.
1117
1118exists_xref(Flag) ->
1119    case code:ensure_loaded(xref) of
1120	{error, _} -> false;
1121	_          -> Flag
1122    end.
1123
1124check_mod(Mod,App,Dir,Ext,IncPath) ->
1125    ObjFile = mod_to_filename(Dir, Mod, Ext),
1126    case file:read_file_info(ObjFile) of
1127	{ok,FileInfo} ->
1128	    LastModTime = FileInfo#file_info.mtime,
1129	    check_module(Mod, Dir, LastModTime, IncPath);
1130	_ ->
1131	    {error, {module_not_found, App, Mod}}
1132    end.
1133
1134mod_to_filename(Dir, Mod, Ext) ->
1135    filename:join(Dir, atom_to_list(Mod) ++ Ext).
1136
1137check_module(Mod, Dir, ObjModTime, IncPath) ->
1138    {SrcDirs,_IncDirs}= smart_guess(Dir,IncPath),
1139    case locate_src(Mod,SrcDirs) of
1140	{ok,_FDir,_File,LastModTime} ->
1141	    if
1142		LastModTime > ObjModTime ->
1143		    {warning, obj_out_of_date};
1144		true ->
1145		    ok
1146	    end;
1147	_ ->
1148	    {warning, source_not_found}
1149    end.
1150
1151locate_src(Mod,[Dir|Dirs]) ->
1152    File = mod_to_filename(Dir, Mod, ".erl"),
1153    case file:read_file_info(File) of
1154	{ok,FileInfo} ->
1155	    LastModTime = FileInfo#file_info.mtime,
1156	    {ok,Dir,File,LastModTime};
1157	_ ->
1158	    locate_src(Mod,Dirs)
1159    end;
1160locate_src(_,[]) ->
1161    false.
1162
1163
1164%%______________________________________________________________________
1165%% smart_guess(Mod, Dir,IncludePath) -> {[Dirs],[IncDirs]}
1166%% Guess the src code and include directory. If dir contains .../ebin
1167%% src-dir should be one of .../src or .../src/e_src
1168%% If dir does not contain .../ebin set dir to the same directory.
1169
1170smart_guess(Dir,IncPath) ->
1171    case reverse(filename:split(Dir)) of
1172	["ebin"|D] ->
1173	    D1 = reverse(D),
1174	    Dirs = [filename:join(D1 ++ ["src"]),
1175		    filename:join(D1 ++ ["src", "e_src"])],
1176	    {Dirs,Dirs ++ IncPath};
1177	_ ->
1178	    {[Dir],[Dir] ++ IncPath}
1179    end.
1180
1181%%______________________________________________________________________
1182%% generate_script(#release,
1183%%                 [{{Name,Vsn},#application}], Flags) ->
1184%%                         ok | {error, Error}
1185%%    Writes a script (a la magnus) to the file File.script
1186%%    and a bootfile to File.boot.
1187
1188generate_script(Output, Release, Appls, Flags) ->
1189    PathFlag = path_flag(Flags),
1190    Variables = get_variables(Flags),
1191    Preloaded = preloaded(),
1192    Mandatory = mandatory_modules(),
1193    Script = {script, {Release#release.name,Release#release.vsn},
1194	      [{preLoaded, Preloaded},
1195	       {progress, preloaded},
1196	       {path, create_mandatory_path(Appls, PathFlag, Variables)},
1197	       {primLoad, Mandatory},
1198	       {kernel_load_completed},
1199	       {progress, kernel_load_completed}] ++
1200	      load_appl_mods(Appls, Mandatory ++ Preloaded,
1201			     PathFlag, Variables) ++
1202	      [{path, create_path(Appls, PathFlag, Variables)}] ++
1203		  create_kernel_procs(Appls) ++
1204		  create_load_appls(Appls) ++
1205		  create_start_appls(Appls) ++
1206		  script_end(lists:member(no_dot_erlang, Flags))
1207	     },
1208
1209    ScriptFile = Output ++ ".script",
1210    case file:open(ScriptFile, [write,{encoding,utf8}]) of
1211	{ok, Fd} ->
1212	    io:format(Fd, "%% ~s\n%% script generated at ~w ~w\n~tp.\n",
1213		      [epp:encoding_to_string(utf8), date(), time(), Script]),
1214	    case file:close(Fd) of
1215		ok ->
1216		    BootFile = Output ++ ".boot",
1217		    case file:write_file(BootFile, term_to_binary(Script)) of
1218			ok ->
1219			    ok;
1220			{error, Reason} ->
1221			    {error, ?MODULE, {open,BootFile,Reason}}
1222		    end;
1223		{error, Reason} ->
1224		    {error, ?MODULE, {close,ScriptFile,Reason}}
1225	    end;
1226	{error, Reason} ->
1227	    {error, ?MODULE, {open,ScriptFile,Reason}}
1228    end.
1229
1230path_flag(Flags) ->
1231    case {member(local,Flags), member(otp_build, Flags)} of
1232	{true, _} -> local;
1233	{_, true} -> otp_build;
1234	{_, _}    -> true
1235    end.
1236
1237get_variables(Flags) ->
1238    case get_flag(variables, Flags) of
1239	{variables, Variables} when is_list(Variables) ->
1240	    valid_variables(Variables);
1241	_ ->
1242	    []
1243    end.
1244
1245valid_variables([{Var,Path}|Variables]) when is_list(Var), is_list(Path) ->
1246    [{Var,rm_tlsl(Path)}|valid_variables(Variables)];
1247valid_variables([{Var,Path}|Variables]) when is_atom(Var), is_list(Path) ->
1248    [{to_list(Var),rm_tlsl(Path)}|valid_variables(Variables)];
1249valid_variables([_|Variables]) ->
1250    valid_variables(Variables);
1251valid_variables(_) ->
1252    [].
1253
1254rm_tlsl(P) -> rm_tlsl1(reverse(P)).
1255rm_tlsl1([$/|P]) -> rm_tlsl1(P);
1256rm_tlsl1(P) -> reverse(P).
1257
1258%%______________________________________________________________________
1259%% Start all applications.
1260%% Do not start applications that are included applications !
1261
1262create_start_appls(Appls) ->
1263    Included = append(map(fun({_,A}) ->
1264				  A#application.includes
1265		      end, Appls)),
1266    create_start_appls(Appls, Included).
1267
1268create_start_appls([{_,A}|T], Incl) ->
1269    App = A#application.name,
1270    case lists:member(App, Incl) of
1271	false when A#application.type == none ->
1272	    create_start_appls(T, Incl);
1273	false when A#application.type == load ->
1274	    create_start_appls(T, Incl);
1275	false ->
1276	    [{apply, {application, start_boot, [App,A#application.type]}} |
1277	     create_start_appls(T, Incl)];
1278	_ ->
1279	    create_start_appls(T, Incl)
1280    end;
1281create_start_appls([], _) ->
1282    [].
1283
1284%%______________________________________________________________________
1285%% Load all applications.
1286
1287create_load_appls([{{kernel,_},_}|T]) -> %Already added !!
1288    create_load_appls(T);
1289create_load_appls([{_,A}|T]) when A#application.type == none ->
1290    create_load_appls(T);
1291create_load_appls([{_,A}|T]) ->
1292    [{apply, {application, load, [pack_app(A)]}} |
1293     create_load_appls(T)];
1294create_load_appls([]) ->
1295    [{progress, applications_loaded}].
1296
1297%%______________________________________________________________________
1298%% The final part of the script.
1299
1300script_end(false) ->  %% Do not skip loading of $HOME/.erlang
1301    [{apply, {c, erlangrc, []}},
1302     {progress, started}];
1303script_end(true) ->   %% Ignore loading of $HOME/.erlang
1304    [{progress, started}].
1305
1306
1307%%-----------------------------------------------------------------
1308%% Function: sort_appls(Appls) -> {ok, Appls'} | throw({error, Error})
1309%% Types: Appls = {{Name, Vsn}, #application}]
1310%% Purpose: Sort applications according to dependencies among
1311%%          applications.  If order doesn't matter, use the same
1312%%          order as in the original list.
1313%% Alg. written by Ulf Wiger 970917 (etxuwig@etxb.ericsson.se)
1314%% Mod. by mbj
1315%%-----------------------------------------------------------------
1316sort_appls(Appls) -> {ok, sort_appls(Appls, [], [], [])}.
1317
1318sort_appls([{N, A}|T], Missing, Circular, Visited) ->
1319    {Name,_Vsn} = N,
1320    {Uses, T1, NotFnd1} = find_all(Name, lists:reverse(A#application.uses),
1321				   T, Visited, [], []),
1322    {Incs, T2, NotFnd2} = find_all(Name, lists:reverse(A#application.includes),
1323				   T1, Visited, [], []),
1324    Missing1 = NotFnd1 ++ NotFnd2 ++ Missing,
1325    case Uses ++ Incs of
1326	[] ->
1327	    %% No more app that must be started before this one is
1328	    %% found; they are all already taken care of (and present
1329	    %% in Visited list)
1330	    [{N, A}|sort_appls(T, Missing1, Circular, [N|Visited])];
1331	L ->
1332	    %% The apps in L must be started before the app.
1333	    %% Check if we have already taken care of some app in L,
1334	    %% in that case we have a circular dependency.
1335	    NewCircular = [N1 || {N1, _} <- L, N2 <- Visited, N1 == N2],
1336	    Circular1 = case NewCircular of
1337			    [] -> Circular;
1338			    _ -> [N | NewCircular] ++ Circular
1339			end,
1340	    %% L must be started before N, try again, with all apps
1341	    %% in L added before N.
1342	    Apps = del_apps(NewCircular, L ++ [{N, A}|T2]),
1343	    sort_appls(Apps, Missing1, Circular1, [N|Visited])
1344    end;
1345sort_appls([], [], [], _) ->
1346    [];
1347sort_appls([], Missing, [], _) ->
1348    %% this has already been checked before, but as we have the info...
1349    throw({error, {undefined_applications, make_set(Missing)}});
1350sort_appls([], [], Circular, _) ->
1351    throw({error, {circular_dependencies, make_set(Circular)}});
1352sort_appls([], Missing, Circular, _) ->
1353    throw({error, {apps, [{circular_dependencies, make_set(Circular)},
1354			  {undefined_applications, make_set(Missing)}]}}).
1355
1356find_all(CheckingApp, [Name|T], L, Visited, Found, NotFound) ->
1357    case find_app(Name, L) of
1358	{value, App} ->
1359	    {_A,R} = App,
1360	    %% It is OK to have a dependecy like
1361	    %% X includes Y, Y uses X.
1362	    case lists:member(CheckingApp, R#application.includes) of
1363		true ->
1364		    case lists:keymember(Name, 1, Visited) of
1365			true ->
1366			    find_all(CheckingApp, T, L, Visited, Found, NotFound);
1367			false ->
1368			    find_all(CheckingApp, T, L, Visited, Found, [Name|NotFound])
1369		    end;
1370		false ->
1371		    find_all(CheckingApp, T, L -- [App], Visited, [App|Found], NotFound)
1372	    end;
1373	false ->
1374	    case lists:keymember(Name, 1, Visited) of
1375		true ->
1376		    find_all(CheckingApp, T, L, Visited, Found, NotFound);
1377		false ->
1378		    find_all(CheckingApp, T, L, Visited, Found, [Name|NotFound])
1379	    end
1380    end;
1381find_all(_CheckingApp, [], L, _Visited, Found, NotFound) ->
1382    {Found, L, NotFound}.
1383
1384find_app(Name, [{{Name,Vsn}, Application}|_]) ->
1385    {value, {{Name,Vsn},Application}};
1386find_app(Name, [_|T]) ->
1387    find_app(Name, T);
1388find_app(_Name, []) ->
1389    false.
1390
1391del_apps([Name|T], L) ->
1392    del_apps(T, lists:keydelete(Name, 1, L));
1393del_apps([], L) ->
1394    L.
1395
1396
1397%%______________________________________________________________________
1398%% Create the load path used in the generated script.
1399%% If PathFlag is true a script intended to be used as a complete
1400%% system (e.g. in an embbeded system), i.e. all applications are
1401%% located under $ROOT/lib.
1402%% Otherwise all paths are set according to dir per application.
1403
1404%% Create the complete path.
1405create_path(Appls, PathFlag, Variables) ->
1406    make_set(map(fun({{Name,Vsn},App}) ->
1407			 cr_path(Name, Vsn, App, PathFlag, Variables)
1408		 end,
1409		 Appls)).
1410
1411%% Create the path to a specific application.
1412%% (The otp_build flag is only used for OTP internal system make)
1413cr_path(Name, Vsn, _, true, []) ->
1414    filename:join(["$ROOT", "lib", to_list(Name) ++ "-" ++ Vsn, "ebin"]);
1415cr_path(Name, Vsn, App, true, Variables) ->
1416    Dir = App#application.dir,
1417    N = to_list(Name),
1418    Tail = [N ++ "-" ++ Vsn, "ebin"],
1419    case variable_dir(Dir, N, Vsn, Variables) of
1420	{ok, VarDir} ->
1421	    filename:join([VarDir] ++ Tail);
1422	_ ->
1423	    filename:join(["$ROOT", "lib"] ++ Tail)
1424    end;
1425cr_path(Name, _, _, otp_build, _) ->
1426    filename:join(["$ROOT", "lib", to_list(Name), "ebin"]);
1427cr_path(_, _, App, _, _) ->
1428    filename:absname(App#application.dir).
1429
1430variable_dir(Dir, Name, Vsn, [{Var,Path}|Variables]) ->
1431    case lists:prefix(Path,Dir) of
1432	true ->
1433	    D0 = strip_prefix(Path, Dir),
1434	    case strip_name_ebin(D0, Name, Vsn) of
1435		{ok, D} ->
1436		    {ok, filename:join(["\$" ++ Var] ++ D)};
1437		_ ->
1438		    %% We know at least that we are located
1439		    %% under the variable dir.
1440		    {ok, filename:join(["\$" ++ Var] ++ D0)}
1441	    end;
1442	_ ->
1443	    variable_dir(Dir, Name, Vsn, Variables)
1444    end;
1445variable_dir(_Dir, _, _, []) ->
1446    false.
1447
1448strip_prefix(Path, Dir) ->
1449    L = length(filename:split(Path)),
1450    lists:nthtail(L, filename:split(Dir)).
1451
1452strip_name_ebin(Dir, Name, Vsn) ->
1453    FullName = Name ++ "-" ++ Vsn,
1454    case reverse(Dir) of
1455	["ebin",Name|D]     -> {ok, reverse(D)};
1456	["ebin",FullName|D] -> {ok, reverse(D)};
1457	_                   -> false
1458    end.
1459
1460%% Create the path to the kernel and stdlib applications.
1461create_mandatory_path(Appls, PathFlag, Variables) ->
1462    Dirs = [kernel, stdlib],
1463    make_set(map(fun({{Name,Vsn}, A}) ->
1464			 case lists:member(Name, Dirs) of
1465			     true ->
1466				 cr_path(Name, Vsn, A, PathFlag, Variables);
1467			     _ ->
1468				 ""
1469			 end
1470		 end,
1471		 Appls)).
1472
1473%%______________________________________________________________________
1474%% Load all modules, except those in Mandatory_modules.
1475
1476load_appl_mods([{{Name,Vsn},A}|Appls], Mand, PathFlag, Variables) ->
1477    Mods = A#application.modules,
1478    load_commands(filter(fun(Mod) -> not member(Mod, Mand) end, Mods),
1479		  cr_path(Name, Vsn, A, PathFlag, Variables)) ++
1480	load_appl_mods(Appls, Mand, PathFlag, Variables);
1481%    [{path, [cr_path(Name, Vsn, A, PathFlag, Variables)]},
1482%     {primLoad, filter(fun(Mod) -> not member(Mod, Mand) end, Mods)} |
1483%     load_appl_mods(Appls, Mand, PathFlag, Variables)];
1484load_appl_mods([], _, _, _) ->
1485    [{progress, modules_loaded}].
1486
1487load_commands(Mods, Path) ->
1488    [{path, [filename:join([Path])]},
1489     {primLoad,lists:sort(Mods)}].
1490
1491%%______________________________________________________________________
1492%% Pack an application to an application term.
1493
1494pack_app(#application{name=Name,vsn=V,id=Id,description=D,modules=M,
1495		      uses=App,includes=Incs,regs=Regs,mod=Mod,start_phases=SF,
1496		      env=Env,maxT=MaxT,maxP=MaxP}) ->
1497    {application, Name,
1498     [{description,D},
1499      {vsn,V},
1500      {id,Id},
1501      {modules, M},
1502      {registered, Regs},
1503      {applications, App},
1504      {included_applications, Incs},
1505      {env, Env},
1506      {maxT, MaxT},
1507      {maxP, MaxP} |
1508      behave([{start_phases,SF},{mod,Mod}])]}.
1509
1510behave([{mod,[]}|T]) ->
1511    behave(T);
1512behave([{start_phases,undefined}|T]) ->
1513    behave(T);
1514behave([H|T]) ->
1515    [H|behave(T)];
1516behave([]) ->
1517    [].
1518
1519mandatory_modules() ->
1520    [error_handler,				%Truly mandatory.
1521
1522     %% Modules that are almost always needed. Listing them here
1523     %% helps the init module to load them faster. Modules not
1524     %% listed here will be loaded by the error_handler module.
1525     %%
1526     %% Keep this list sorted.
1527     application,
1528     application_controller,
1529     application_master,
1530     code,
1531     code_server,
1532     erl_eval,
1533     erl_lint,
1534     erl_parse,
1535     error_logger,
1536     ets,
1537     file,
1538     filename,
1539     file_server,
1540     file_io_server,
1541     gen,
1542     gen_event,
1543     gen_server,
1544     heart,
1545     kernel,
1546     logger,
1547     logger_filters,
1548     logger_server,
1549     logger_backend,
1550     logger_config,
1551     logger_simple_h,
1552     lists,
1553     proc_lib,
1554     supervisor
1555    ].
1556
1557%%______________________________________________________________________
1558%% This is the modules that are preloaded into the Erlang system.
1559
1560preloaded() ->
1561    lists:sort(
1562      ?ESOCK_MODS ++
1563          [atomics,counters,erl_init,erl_prim_loader,erl_tracer,erlang,
1564           erts_code_purger,erts_dirty_process_signal_handler,
1565           erts_internal,erts_literal_area_collector,
1566           init,persistent_term,prim_buffer,prim_eval,prim_file,
1567           prim_inet,prim_zip,zlib]).
1568
1569%%______________________________________________________________________
1570%% This is the erts binaries that should *not* be part of a systool:make_tar package
1571
1572erts_binary_filter() ->
1573    Cmds = ["typer", "dialyzer", "ct_run", "yielding_c_fun", "erlc"],
1574    case os:type() of
1575        {unix,_} -> Cmds;
1576        {win32,_} -> [ [Cmd, ".exe"] || Cmd <- Cmds]
1577    end.
1578
1579%%______________________________________________________________________
1580%% Kernel processes; processes that are specially treated by the init
1581%% process. If a kernel process terminates the whole system terminates.
1582%% kernel_processes() -> [{Name, Mod, Func, Args}]
1583%% where Args is a term or a fun taking the list of applications as arg.
1584
1585kernel_processes() ->
1586    [{heart, heart, start, []},
1587     {logger, logger_server, start_link, []},
1588     {application_controller, application_controller, start,
1589      fun(Appls) ->
1590              [{_,App}] = filter(fun({{kernel,_},_App}) -> true;
1591                                    (_)                 -> false
1592                                 end,
1593                                 Appls),
1594              [pack_app(App)]
1595      end}
1596    ].
1597
1598%%______________________________________________________________________
1599%% Create the kernel processes.
1600
1601create_kernel_procs(Appls) ->
1602    map(fun({Name,Mod,Func,Args}) when is_function(Args) ->
1603                {kernelProcess, Name, {Mod, Func, Args(Appls)}};
1604           ({Name,Mod,Func,Args}) ->
1605                {kernelProcess, Name, {Mod, Func, Args}}
1606        end,
1607        kernel_processes()) ++
1608    [{progress, init_kernel_started}].
1609
1610%%______________________________________________________________________
1611%% Make a tar file of the release.
1612%% The tar file contains:
1613%%         lib/App-Vsn/ebin
1614%%                    /priv
1615%%                   [/src]
1616%%                   [/include]
1617%%                   [/doc]
1618%%                   [/examples]
1619%%                   [/...]
1620%%         Variable1.tar.gz
1621%%         ...
1622%%         VariableN.tar.gz
1623%%         releases/RelName.rel
1624%%                  RelVsn/start.boot
1625%%                         relup
1626%%                         sys.config
1627%%                         sys.config.src
1628%%         erts-EVsn[/bin]
1629%%
1630%% The VariableN.tar.gz files can also be stored as own files not
1631%% included in the main tar file or they can be omitted using
1632%% the var_tar option.
1633
1634mk_tar(RelName, Release, Appls, Flags, Path1) ->
1635    TarName = case get_outdir(Flags) of
1636		  "" ->
1637		      RelName ++ ".tar.gz";
1638		  OutDir ->
1639		      filename:join(OutDir, filename:basename(RelName))
1640			  ++ ".tar.gz"
1641	      end,
1642    Tar = open_main_tar(TarName),
1643    case catch mk_tar(Tar, RelName, Release, Appls, Flags, Path1) of
1644	{error,Error} ->
1645	    _ = del_tar(Tar, TarName),
1646	    {error,?MODULE,Error};
1647	{'EXIT',Reason} ->
1648	    _ = del_tar(Tar, TarName),
1649	    {error,?MODULE,Reason};
1650	_ ->
1651	    case erl_tar:close(Tar) of
1652		ok -> ok;
1653		{error,Reason} -> {error,?MODULE,{close,TarName,Reason}}
1654	    end
1655    end.
1656
1657open_main_tar(TarName) ->
1658    case catch open_tar(TarName) of
1659	{error, Error} ->
1660	    throw({error,?MODULE,Error});
1661	Tar ->
1662	    Tar
1663    end.
1664
1665mk_tar(Tar, RelName, Release, Appls, Flags, Path1) ->
1666    Variables = get_variables(Flags),
1667    add_applications(Appls, Tar, Variables, Flags, false),
1668    add_variable_tars(Variables, Appls, Tar, Flags),
1669    add_system_files(Tar, RelName, Release, Path1),
1670    add_erts_bin(Tar, Release, Flags),
1671    add_additional_files(Tar, Flags).
1672
1673add_additional_files(Tar, Flags) ->
1674    case get_flag(extra_files, Flags) of
1675        {extra_files, ToAdd} ->
1676            [add_to_tar(Tar, From, To) || {From, To} <- ToAdd];
1677        _ ->
1678            ok
1679    end.
1680
1681add_applications(Appls, Tar, Variables, Flags, Var) ->
1682    Res = foldl(fun({{Name,Vsn},App}, Errs) ->
1683		  case catch add_appl(to_list(Name), Vsn, App,
1684				      Tar, Variables, Flags, Var) of
1685		      ok ->
1686			  Errs;
1687		      {error, What} ->
1688			  [{error_add_appl, {Name,What}}|Errs]
1689		  end
1690	       end, [], Appls),
1691    case Res of
1692	[] ->
1693	    ok;
1694	Errors ->
1695	    throw({error, Errors})
1696    end.
1697
1698%%______________________________________________________________________
1699%% Create a tar file for each Variable directory.
1700%% Deletes the temporary tar file.
1701
1702add_variable_tars([Variable|Variables], Appls, Tar, Flags) ->
1703    add_variable_tar(Variable, Appls, Tar, Flags),
1704    add_variable_tars(Variables, Appls, Tar, Flags);
1705add_variable_tars([], _, _, _) ->
1706    ok.
1707
1708add_variable_tar({Variable,P}, Appls, Tar, Flags) ->
1709    case var_tar_flag(Flags) of
1710	omit ->
1711	    ok;
1712	Flag ->
1713	    TarName = Variable ++ ".tar.gz",
1714	    VarTar = open_tar(TarName),
1715	    case catch add_applications(Appls, VarTar, [{Variable,P}],
1716					Flags, Variable) of
1717		ok when Flag == include ->
1718		    close_tar(VarTar,TarName),
1719		    add_to_tar(Tar, TarName, TarName),
1720		    del_file(TarName);
1721		ok when Flag == ownfile ->
1722		    close_tar(VarTar,TarName);
1723		Error ->
1724		    _ = del_tar(VarTar, TarName),
1725		    throw(Error)
1726	    end
1727    end.
1728
1729var_tar_flag(Flags) ->
1730    case get_flag(var_tar, Flags) of
1731	{var_tar, Flag} ->
1732	    case member(Flag, [include, ownfile, omit]) of
1733		true -> Flag;
1734		_    -> include
1735	    end;
1736	_ ->
1737	    include
1738    end.
1739
1740%%______________________________________________________________________
1741%% Add all "other" files to Dir/releases/Svsn
1742%% add_system_files(Tar,Name,release#,Flags) ->
1743%%   ok | throw({error,Error})
1744
1745add_system_files(Tar, RelName, Release, Path1) ->
1746    SVsn = Release#release.vsn,
1747    RelName0 = filename:basename(RelName),
1748
1749    RelVsnDir = filename:join("releases", SVsn),
1750
1751    %% OTP-9746: store rel file in releases/<vsn>
1752    %% Adding rel file to
1753    %% 1) releases directory - so it can be easily extracted
1754    %%    separately (see release_handler:unpack_release)
1755    %% 2) releases/<vsn> - so the file must not be explicitly moved
1756    %%    after unpack.
1757    add_to_tar(Tar, RelName ++ ".rel",
1758	       filename:join("releases", RelName0 ++ ".rel")),
1759    add_to_tar(Tar, RelName ++ ".rel",
1760	       filename:join(RelVsnDir, RelName0 ++ ".rel")),
1761
1762
1763    %% OTP-6226 Look for the system files not only in cwd
1764    %% --
1765    %% (well, actually the boot file was looked for in the same
1766    %% directory as RelName, which is not necessarily the same as cwd)
1767    %% --
1768    %% but also in the path specfied as an option to systools:make_tar
1769    %% (but make sure to search the RelName directory and cwd first)
1770    Path = case filename:dirname(RelName) of
1771	       "." ->
1772		   ["."|Path1];
1773	       RelDir ->
1774		   [RelDir, "."|Path1]
1775	   end,
1776
1777    case lookup_file("start.boot", Path) of
1778        false ->
1779            case lookup_file(RelName0 ++ ".boot", Path) of
1780                false ->
1781                    throw({error, {tar_error, {add, boot, RelName, enoent}}});
1782                Boot ->
1783                    add_to_tar(Tar, Boot, filename:join(RelVsnDir, "start.boot"))
1784            end;
1785        Boot ->
1786            add_to_tar(Tar, Boot, filename:join(RelVsnDir, "start.boot"))
1787    end,
1788
1789    case lookup_file("relup", Path) of
1790	false ->
1791	    ignore;
1792	Relup ->
1793	    check_relup(Relup),
1794	    add_to_tar(Tar, Relup, filename:join(RelVsnDir, "relup"))
1795    end,
1796
1797    case lookup_file("sys.config.src", Path) of
1798        false ->
1799            case lookup_file("sys.config", Path) of
1800                false ->
1801                    ignore;
1802                Sys ->
1803	            check_sys_config(Sys),
1804	            add_to_tar(Tar, Sys, filename:join(RelVsnDir, "sys.config"))
1805            end;
1806        SysSrc ->
1807            add_to_tar(Tar, SysSrc, filename:join(RelVsnDir, "sys.config.src"))
1808    end,
1809    ok.
1810
1811lookup_file(Name, [Dir|Path]) ->
1812    File = filename:join(Dir, Name),
1813    case filelib:is_file(File) of
1814	true ->
1815	    File;
1816	false ->
1817	    lookup_file(Name, Path)
1818    end;
1819lookup_file(_Name, []) ->
1820    false.
1821
1822%% Check that relup can be parsed and has expected format
1823check_relup(File) ->
1824    case file:consult(File) of
1825	{ok,[{Vsn,UpFrom,DownTo}]} when is_list(Vsn), is_integer(hd(Vsn)),
1826					is_list(UpFrom), is_list(DownTo) ->
1827	    ok;
1828	{ok,_} ->
1829	    throw({error,{tar_error,{add,"relup",[invalid_format]}}});
1830	Other ->
1831	    throw({error,{tar_error,{add,"relup",[Other]}}})
1832    end.
1833
1834%% Check that sys.config can be parsed and has expected format
1835check_sys_config(File) ->
1836    case file:consult(File) of
1837	{ok,[SysConfig]} ->
1838	    case lists:all(fun({App,KeyVals}) when is_atom(App),
1839						   is_list(KeyVals)->
1840				   true;
1841			      (OtherConfig) when is_list(OtherConfig),
1842						 is_integer(hd(OtherConfig)) ->
1843				   true;
1844			      (_) ->
1845				   false
1846			   end,
1847			   SysConfig) of
1848		true ->
1849		    ok;
1850		false ->
1851		    throw({error,{tar_error,
1852				  {add,"sys.config",[invalid_format]}}})
1853	    end;
1854	{ok,_} ->
1855	    throw({error,{tar_error,{add,"sys.config",[invalid_format]}}});
1856	Other ->
1857	    throw({error,{tar_error,{add,"sys.config",[Other]}}})
1858    end.
1859
1860%%______________________________________________________________________
1861%% Add either a application located under a variable dir or all other
1862%% applications to a tar file.
1863%% add_appl(Name,Vsn,application#,Tar,Variables,Flags,Var) ->
1864%%    ok | {error,Error}
1865
1866add_appl(Name, Vsn, App, Tar, Variables, Flags, Var) ->
1867    AppDir = App#application.dir,
1868    case add_to(AppDir,Name,Vsn,Variables,Var) of
1869	false ->
1870	    ok;
1871	{ok, ToDir} ->
1872	    ADir = appDir(AppDir),
1873	    add_priv(ADir, ToDir, Tar),
1874	    case get_flag(dirs,Flags) of
1875		{dirs,Dirs} ->
1876		    add_dirs(ADir, Dirs, ToDir, Tar);
1877		_ ->
1878		    ok
1879	    end,
1880	    BinDir = filename:join(ToDir, "ebin"),
1881	    add_to_tar(Tar,
1882		       filename:join(AppDir, Name ++ ".app"),
1883		       filename:join(BinDir, Name ++ ".app")),
1884	    add_modules(map(fun(Mod) -> to_list(Mod) end,
1885			    App#application.modules),
1886			Tar,
1887			AppDir,
1888			BinDir,
1889			code:objfile_extension())
1890    end.
1891
1892%%______________________________________________________________________
1893%% If an application directory contains a Variable (in AppDir) the
1894%% application will be placed in the tar file (if it is this Variable
1895%% we corrently is actually storing).
1896
1897add_to(AppDir,Name,Vsn,Variables,Variable) ->
1898    case var_dir(AppDir,Name,Vsn,Variables) of
1899	{ok, Variable, RestPath} ->
1900	    {ok, filename:join(RestPath ++ [Name ++ "-" ++ Vsn])};
1901	{ok, _, _} ->
1902	    false;
1903	_ when Variable == false ->
1904	    {ok, filename:join("lib", Name ++ "-" ++ Vsn)};
1905	_ ->
1906	    false
1907    end.
1908
1909var_dir(Dir, Name, Vsn, [{Var,Path}|Variables]) ->
1910    case lists:prefix(Path,Dir) of
1911	true ->
1912	    D0 = strip_prefix(Path, Dir),
1913	    case strip_name_ebin(D0, Name, Vsn) of
1914		{ok, D} ->
1915		    {ok, Var, D};
1916		_ ->
1917		    false
1918	    end;
1919	_ ->
1920	    var_dir(Dir, Name, Vsn, Variables)
1921    end;
1922var_dir(_Dir, _, _, []) ->
1923    false.
1924
1925appDir(AppDir) ->
1926    case filename:basename(AppDir) of
1927	"ebin" -> filename:dirname(AppDir);
1928	_ -> AppDir
1929    end.
1930
1931add_modules(Modules, Tar, AppDir, ToDir, Ext) ->
1932    foreach(fun(Mod) ->
1933		    add_to_tar(Tar,
1934			       filename:join(AppDir, Mod ++ Ext),
1935			       filename:join(ToDir, Mod ++ Ext))
1936	    end, Modules).
1937
1938%%
1939%% Add own specified directories to include in the release.
1940%% If not found, skip it.
1941%%
1942add_dirs(AppDir, Dirs, ToDir, Tar) ->
1943    foreach(fun(Dir) -> catch add_dir(AppDir, to_list(Dir), ToDir, Tar) end,
1944	    Dirs).
1945
1946add_dir(TopDir, Dir, ToDir, Tar) ->
1947    FromD = filename:join(TopDir, Dir),
1948    case dirp(FromD) of
1949	true ->
1950	    add_to_tar(Tar, FromD, filename:join(ToDir, Dir));
1951	_ ->
1952	    ok
1953    end.
1954
1955%%
1956%% Add the priv dir if it exists.
1957
1958add_priv(ADir, ToDir, Tar) ->
1959    Priv = filename:join(ADir, "priv"),
1960    case dirp(Priv) of
1961	true ->
1962	    add_to_tar(Tar, Priv, filename:join(ToDir, "priv"));
1963	_ ->
1964	    ok
1965    end.
1966
1967add_erts_bin(Tar, Release, Flags) ->
1968    case {get_flag(erts,Flags),member(erts_all,Flags)} of
1969	{{erts,ErtsDir},true} ->
1970            add_erts_bin(Tar, Release, ErtsDir, []);
1971	{{erts,ErtsDir},false} ->
1972            add_erts_bin(Tar, Release, ErtsDir, erts_binary_filter());
1973	_ ->
1974	    ok
1975    end.
1976
1977add_erts_bin(Tar, Release, ErtsDir, Filters) ->
1978    FlattenedFilters = [filename:flatten(Filter) || Filter <- Filters],
1979    EVsn = Release#release.erts_vsn,
1980    FromDir = filename:join([to_list(ErtsDir),
1981                             "erts-" ++ EVsn, "bin"]),
1982    ToDir = filename:join("erts-" ++ EVsn, "bin"),
1983    {ok, Bins} = file:list_dir(FromDir),
1984    [add_to_tar(Tar, filename:join(FromDir,Bin), filename:join(ToDir,Bin))
1985     || Bin <- Bins, not lists:member(Bin, FlattenedFilters)],
1986    ok.
1987
1988%%______________________________________________________________________
1989%% Tar functions.
1990
1991open_tar(TarName) ->
1992    case erl_tar:open(TarName, [write, compressed]) of
1993	{ok, Tar} ->
1994	    Tar;
1995	{error, Error} ->
1996	    throw({error,{tar_error, {open, TarName, Error}}})
1997    end.
1998
1999close_tar(Tar,File) ->
2000    case erl_tar:close(Tar) of
2001	ok -> ok;
2002	{error,Reason} -> throw({error,{close,File,Reason}})
2003    end.
2004
2005del_tar(Tar, TarName) ->
2006    _ = erl_tar:close(Tar),
2007    file:delete(TarName).
2008
2009add_to_tar(Tar, FromFile, ToFile) ->
2010    case catch erl_tar:add(Tar, FromFile, ToFile, [compressed, dereference]) of
2011	ok -> ok;
2012        {'EXIT', Reason} ->
2013            throw({error, {tar_error, {add, FromFile, Reason}}});
2014	{error, Error} ->
2015	    throw({error, {tar_error, {add, FromFile, Error}}})
2016    end.
2017
2018%%______________________________________________________________________
2019%%______________________________________________________________________
2020%% utilities!
2021
2022make_set([]) -> [];
2023make_set([""|T]) -> % Ignore empty items.
2024    make_set(T);
2025make_set([H|T]) ->
2026    [H | [ Y || Y<- make_set(T),
2027		Y =/= H]].
2028
2029to_list(A) when is_atom(A) -> atom_to_list(A);
2030to_list(L)                 -> L.
2031
2032mk_path(Path0) ->
2033    Path1 = map(fun(Dir) when is_atom(Dir) -> atom_to_list(Dir);
2034		   (Dir)                   -> Dir
2035		end, Path0),
2036    systools_lib:get_path(Path1).
2037
2038%% duplicates([Tuple]) -> List of pairs where
2039%%    element(1, T1) == element(1, T2) and  where T1 and T2 are
2040%%    taken from [Tuple]
2041
2042duplicates(X) -> duplicates(keysort(1,X), []).
2043
2044duplicates([H1,H2|T], L) ->
2045    case {element(1,H1),element(1,H2)} of
2046	{X,X} -> duplicates([H2|T],[{H1,H2}|L]);
2047        _     -> duplicates([H2|T],L)
2048    end;
2049duplicates(_, L) -> L.
2050
2051%% read_file(File, Path) -> {ok, Term, FullName} | {error, Error}
2052%% read a file and check the syntax, i.e. that it contains a correct
2053%% Erlang term.
2054
2055read_file(File, Path) ->
2056    case file:path_open(Path, File, [read]) of
2057	{ok, Stream, FullName} ->
2058	    Return = case systools_lib:read_term_from_stream(Stream, File) of
2059			 {ok, Term} ->
2060			     {ok, Term, FullName};
2061			 Other ->
2062			     Other
2063		     end,
2064	    case file:close(Stream) of
2065		ok -> Return;
2066		{error, Error} -> {error, {close,File,Error}}
2067	    end;
2068	_Other ->
2069	    {error, {not_found, File}}
2070    end.
2071
2072del_file(File) ->
2073    case file:delete(File) of
2074	ok -> ok;
2075	{error, Error} ->
2076	    throw({error, {delete, File, Error}})
2077    end.
2078
2079dirp(Dir) ->
2080    case file:read_file_info(Dir) of
2081	{ok, FileInfo} -> FileInfo#file_info.type == directory;
2082	_ ->              false
2083    end.
2084
2085%% Create the include path. Assumptions about the code path is done
2086%% and an include directory is added.
2087%% Add the official include dir for each found application first in
2088%% path !!
2089%% If .../ebin exists in a path an .../include directory is assumed to
2090%% exist at the same level. If .../ebin is not existing the .../include
2091%% directory is assumed anyhow.
2092%% Local includes are added for each application later on.
2093
2094create_include_path(Appls, Path) ->
2095    FoundAppDirs = map(fun({_,A}) -> A#application.dir end, Appls),
2096    map(fun(Dir) ->
2097		case reverse(filename:split(Dir)) of
2098		    ["ebin"|D] ->
2099			filename:join(reverse(D) ++ ["include"]);
2100		    _ ->
2101			filename:join(Dir, "include")
2102		end
2103	end,
2104	FoundAppDirs ++ no_dupl(Path, FoundAppDirs)).
2105
2106no_dupl([Dir|Path], FoundAppDirs) ->
2107    case member(Dir, FoundAppDirs) of
2108	true ->
2109	    no_dupl(Path, FoundAppDirs);
2110	_ ->
2111	    [Dir|no_dupl(Path, FoundAppDirs)]
2112    end;
2113no_dupl([], _) ->
2114    [].
2115
2116is_app_type(permanent) -> true;
2117is_app_type(transient) -> true;
2118is_app_type(temporary) -> true;
2119is_app_type(none) -> true;
2120is_app_type(load) -> true;
2121is_app_type(_) -> false.
2122
2123% check if a term is a string.
2124
2125string_p(S) ->
2126    case unicode:characters_to_list(S) of
2127	S -> true;
2128	_ -> false
2129    end.
2130
2131% check if a term is a list of two tuples with the first
2132% element as an atom.
2133
2134t_list_p([{A,_}|T]) when is_atom(A) -> t_list_p(T);
2135t_list_p([])                        -> true;
2136t_list_p(_)                         -> false.
2137
2138% check if a term is a list of atoms.
2139
2140a_list_p([A|T]) when is_atom(A) -> a_list_p(T);
2141a_list_p([])                    -> true;
2142a_list_p(_)                     -> false.
2143
2144%% Get a key-value tuple flag from a list.
2145
2146get_flag(F,[{F,D}|_]) -> {F,D};
2147get_flag(F,[_|Fs])    -> get_flag(F,Fs);
2148get_flag(_,_)         -> false.
2149
2150%% Check Options for make_script
2151check_args_script(Args) ->
2152    cas(Args, []).
2153
2154cas([], X) ->
2155    X;
2156%%% path ---------------------------------------------------------------
2157cas([{path, P} | Args], X) when is_list(P) ->
2158    case check_path(P) of
2159	ok ->
2160	    cas(Args, X);
2161	error ->
2162	    cas(Args, X++[{path,P}])
2163    end;
2164%%% silent -------------------------------------------------------------
2165cas([silent | Args], X) ->
2166    cas(Args, X);
2167%%% local --------------------------------------------------------------
2168cas([local | Args], X) ->
2169    cas(Args, X);
2170%%% src_tests -------------------------------------------------------
2171cas([src_tests | Args], X) ->
2172    cas(Args, X);
2173%%% variables ----------------------------------------------------------
2174cas([{variables, V} | Args], X) when is_list(V) ->
2175    case check_vars(V) of
2176	ok ->
2177	    cas(Args, X);
2178	error ->
2179	    cas(Args, X++[{variables, V}])
2180    end;
2181%%% exref --------------------------------------------------------------
2182cas([exref | Args], X)  ->
2183    cas(Args, X);
2184%%% exref Apps ---------------------------------------------------------
2185cas([{exref, Apps} | Args], X) when is_list(Apps) ->
2186    case check_apps(Apps) of
2187	ok ->
2188	    cas(Args, X);
2189	error ->
2190	    cas(Args, X++[{exref, Apps}])
2191    end;
2192%%% outdir Dir ---------------------------------------------------------
2193cas([{outdir, Dir} | Args], X) when is_list(Dir) ->
2194    cas(Args, X);
2195%%% otp_build (secret, not documented) ---------------------------------
2196cas([otp_build | Args], X) ->
2197    cas(Args, X);
2198%%% warnings_as_errors -------------------------------------------------
2199cas([warnings_as_errors | Args], X) ->
2200    cas(Args, X);
2201%%% no_warn_sasl -------------------------------------------------------
2202cas([no_warn_sasl | Args], X) ->
2203    cas(Args, X);
2204%%% no_module_tests (kept for backwards compatibility, but ignored) ----
2205cas([no_module_tests | Args], X) ->
2206    cas(Args, X);
2207cas([no_dot_erlang | Args], X) ->
2208    cas(Args, X);
2209%% set the name of the script and boot file to create
2210cas([{script_name, Name} | Args], X) when is_list(Name) ->
2211    cas(Args, X);
2212
2213%%% ERROR --------------------------------------------------------------
2214cas([Y | Args], X) ->
2215    cas(Args, X++[Y]).
2216
2217
2218
2219%% Check Options for make_tar
2220check_args_tar(Args) ->
2221    cat(Args, []).
2222
2223cat([], X) ->
2224    X;
2225%%% path ---------------------------------------------------------------
2226cat([{path, P} | Args], X) when is_list(P) ->
2227    case check_path(P) of
2228	ok ->
2229	    cat(Args, X);
2230	error ->
2231	    cat(Args, X++[{path,P}])
2232    end;
2233%%% silent -------------------------------------------------------------
2234cat([silent | Args], X) ->
2235    cat(Args, X);
2236%%% dirs ---------------------------------------------------------------
2237cat([{dirs, D} | Args], X) ->
2238    case check_dirs(D) of
2239	ok ->
2240	    cat(Args, X);
2241	error ->
2242	    cat(Args, X++[{dirs, D}])
2243    end;
2244%%% erts ---------------------------------------------------------------
2245cat([{erts, E} | Args], X) when is_list(E)->
2246    cat(Args, X);
2247cat([erts_all | Args], X) ->
2248    cat(Args, X);
2249%%% src_tests ----------------------------------------------------
2250cat([src_tests | Args], X) ->
2251    cat(Args, X);
2252%%% variables ----------------------------------------------------------
2253cat([{variables, V} | Args], X) when is_list(V) ->
2254    case check_vars(V) of
2255	ok ->
2256	    cat(Args, X);
2257	error ->
2258	    cat(Args, X++[{variables, V}])
2259    end;
2260%%% var_tar ------------------------------------------------------------
2261cat([{var_tar, VT} | Args], X) when VT == include;
2262                                    VT == ownfile;
2263                                    VT == omit ->
2264    cat(Args, X);
2265%%% exref --------------------------------------------------------------
2266cat([exref | Args], X)  ->
2267    cat(Args, X);
2268%%% exref Apps ---------------------------------------------------------
2269cat([{exref, Apps} | Args], X) when is_list(Apps) ->
2270    case check_apps(Apps) of
2271	ok ->
2272	    cat(Args, X);
2273	error ->
2274	    cat(Args, X++[{exref, Apps}])
2275    end;
2276%%% outdir Dir ---------------------------------------------------------
2277cat([{outdir, Dir} | Args], X) when is_list(Dir) ->
2278    cat(Args, X);
2279%%% otp_build (secret, not documented) ---------------------------------
2280cat([otp_build | Args], X)  ->
2281    cat(Args, X);
2282%%% warnings_as_errors ----
2283cat([warnings_as_errors | Args], X) ->
2284    cat(Args, X);
2285%%% no_warn_sasl ----
2286cat([no_warn_sasl | Args], X) ->
2287    cat(Args, X);
2288%%% no_module_tests (kept for backwards compatibility, but ignored) ----
2289cat([no_module_tests | Args], X) ->
2290    cat(Args, X);
2291cat([{extra_files, ExtraFiles} | Args], X) when is_list(ExtraFiles) ->
2292    cat(Args, X);
2293
2294%%% ERROR --------------------------------------------------------------
2295cat([Y | Args], X) ->
2296    cat(Args, X++[Y]).
2297
2298check_path([]) ->
2299    ok;
2300check_path([H|T]) when is_list(H) ->
2301    check_path(T);
2302check_path([_H|_T]) ->
2303    error.
2304
2305check_dirs([]) ->
2306    ok;
2307check_dirs([H|T]) when is_atom(H) ->
2308    check_dirs(T);
2309check_dirs([_H|_T]) ->
2310    error.
2311
2312check_vars([]) ->
2313    ok;
2314check_vars([{Name, Dir} | T]) ->
2315    if
2316	is_atom(Name), is_list(Dir) ->
2317	    check_vars(T);
2318	is_list(Name), is_list(Dir) ->
2319	    check_vars(T);
2320	true ->
2321	    error
2322    end;
2323check_vars(_) ->
2324    error.
2325
2326check_apps([]) ->
2327    ok;
2328check_apps([H|T]) when is_atom(H) ->
2329    check_apps(T);
2330check_apps(_) ->
2331    error.
2332
2333%% Format error
2334
2335format_error(badly_formatted_release) ->
2336    io_lib:format("Syntax error in the release file~n",[]);
2337format_error({illegal_name, Name}) ->
2338    io_lib:format("Illegal name (~tp) in the release file~n",[Name]);
2339format_error({illegal_form, Form}) ->
2340    io_lib:format("Illegal tag in the release file: ~tp~n",[Form]);
2341format_error({missing_parameter,Par}) ->
2342    io_lib:format("Missing parameter (~p) in the release file~n",[Par]);
2343format_error({illegal_applications,Names}) ->
2344    io_lib:format("Illegal applications in the release file: ~p~n",
2345		  [Names]);
2346format_error({missing_mandatory_app,Name}) ->
2347    io_lib:format("Mandatory application ~w must be specified in the release file~n",
2348		  [Name]);
2349format_error({mandatory_app,Name,Type}) ->
2350    io_lib:format("Mandatory application ~w must be of type 'permanent' in the release file. Is '~p'.~n",
2351		  [Name,Type]);
2352format_error({duplicate_register,Dups}) ->
2353    io_lib:format("Duplicated register names: ~n~ts",
2354		  [map(fun({{Reg,App1,_,_},{Reg,App2,_,_}}) ->
2355			       io_lib:format("\t~tw registered in ~w and ~w~n",
2356					     [Reg,App1,App2])
2357		       end, Dups)]);
2358format_error({undefined_applications,Apps}) ->
2359    io_lib:format("Undefined applications: ~p~n",[Apps]);
2360format_error({duplicate_modules,Dups}) ->
2361    io_lib:format("Duplicated modules: ~n~ts",
2362		  [map(fun({{Mod,App1,_},{Mod,App2,_}}) ->
2363			       io_lib:format("\t~w specified in ~w and ~w~n",
2364					     [Mod,App1,App2])
2365		       end, Dups)]);
2366format_error({included_and_used, Dups}) ->
2367    io_lib:format("Applications both used and included: ~p~n",[Dups]);
2368format_error({duplicate_include, Dups}) ->
2369    io_lib:format("Duplicated application included: ~n~ts",
2370		  [map(fun({{Name,App1,_,_},{Name,App2,_,_}}) ->
2371			       io_lib:format("\t~w included in ~w and ~w~n",
2372					     [Name,App1,App2])
2373		       end, Dups)]);
2374format_error({modules,ModErrs}) ->
2375    format_errors(ModErrs);
2376format_error({circular_dependencies,Apps}) ->
2377    io_lib:format("Circular dependencies among applications: ~p~n",[Apps]);
2378format_error({not_found,File}) ->
2379    io_lib:format("File not found: ~tp~n",[File]);
2380format_error({parse,File,{Line,Mod,What}}) ->
2381    Str = Mod:format_error(What),
2382    io_lib:format("~ts:~w: ~ts\n",[File, Line, Str]);
2383format_error({read,File}) ->
2384    io_lib:format("Cannot read ~tp~n",[File]);
2385format_error({open,File,Error}) ->
2386    io_lib:format("Cannot open ~tp - ~ts~n",
2387		  [File,file:format_error(Error)]);
2388format_error({close,File,Error}) ->
2389    io_lib:format("Cannot close ~tp - ~ts~n",
2390		  [File,file:format_error(Error)]);
2391format_error({delete,File,Error}) ->
2392    io_lib:format("Cannot delete ~tp - ~ts~n",
2393		  [File,file:format_error(Error)]);
2394format_error({tar_error,What}) ->
2395    form_tar_err(What);
2396format_error({warnings_treated_as_errors,Warnings}) ->
2397    io_lib:format("Warnings being treated as errors:~n~ts",
2398                  [map(fun(W) -> form_warn("",W) end, Warnings)]);
2399format_error(ListOfErrors) when is_list(ListOfErrors) ->
2400    format_errors(ListOfErrors);
2401format_error(E) -> io_lib:format("~tp~n",[E]).
2402
2403format_errors(ListOfErrors) ->
2404    map(fun({error,E}) -> form_err(E);
2405	   (E)         -> form_err(E)
2406	end, ListOfErrors).
2407
2408form_err({bad_application_name,{Name,Found}}) ->
2409    io_lib:format("~p: Mismatched application id: ~p~n",[Name,Found]);
2410form_err({error_reading, {Name, What}}) ->
2411    io_lib:format("~p: ~ts~n",[Name,form_reading(What)]);
2412form_err({module_not_found,App,Mod}) ->
2413    io_lib:format("~w: Module (~w) not found~n",[App,Mod]);
2414form_err({error_add_appl, {Name, {tar_error, What}}}) ->
2415    io_lib:format("~p: ~ts~n",[Name,form_tar_err(What)]);
2416form_err(E) ->
2417    io_lib:format("~tp~n",[E]).
2418
2419form_reading({not_found,File}) ->
2420    io_lib:format("File not found: ~tp~n",[File]);
2421form_reading({application_vsn, {Name,Vsn}}) ->
2422    io_lib:format("Application ~ts with version ~tp not found~n",[Name, Vsn]);
2423form_reading({parse,File,{Line,Mod,What}}) ->
2424    Str = Mod:format_error(What),
2425    io_lib:format("~ts:~w: ~ts\n",[File, Line, Str]);
2426form_reading({read,File}) ->
2427    io_lib:format("Cannot read ~tp~n",[File]);
2428form_reading({{bad_param, P},_}) ->
2429    io_lib:format("Bad parameter in .app file: ~tp~n",[P]);
2430form_reading({{missing_param,P},_}) ->
2431    io_lib:format("Missing parameter in .app file: ~p~n",[P]);
2432form_reading({badly_formatted_application,_}) ->
2433    io_lib:format("Syntax error in .app file~n",[]);
2434form_reading({override_include,Apps}) ->
2435    io_lib:format("Tried to include not (in .app file) specified applications: ~p~n",
2436		  [Apps]);
2437form_reading({no_valid_version, {{_, SVsn}, {_, File, FVsn}}}) ->
2438    io_lib:format("No valid version (~tp) of .app file found. Found file ~tp with version ~tp~n",
2439		  [SVsn, File, FVsn]);
2440form_reading({parse_error, {File, Line, Error}}) ->
2441    io_lib:format("Parse error in file: ~tp.  Line: ~w  Error: ~tp; ~n", [File, Line, Error]);
2442form_reading(W) ->
2443    io_lib:format("~tp~n",[W]).
2444
2445form_tar_err({open, File, Error}) ->
2446    io_lib:format("Cannot open tar file ~ts - ~ts~n",
2447		  [File, erl_tar:format_error(Error)]);
2448form_tar_err({add, boot, RelName, enoent}) ->
2449    io_lib:format("Cannot find file start.boot or ~ts to add to tar file - ~ts~n",
2450		  [RelName, erl_tar:format_error(enoent)]);
2451form_tar_err({add, File, Error}) ->
2452    io_lib:format("Cannot add file ~ts to tar file - ~ts~n",
2453		  [File, erl_tar:format_error(Error)]).
2454
2455%% Format warning
2456
2457format_warning(Warnings) ->
2458    map(fun({warning,W}) -> form_warn("*WARNING* ", W) end, Warnings).
2459
2460form_warn(Prefix, {source_not_found,{Mod,App,_}}) ->
2461    io_lib:format("~ts~w: Source code not found: ~w.erl~n",
2462		  [Prefix,App,Mod]);
2463form_warn(Prefix, {{parse_error, File},{_,_,App,_,_}}) ->
2464    io_lib:format("~ts~w: Parse error: ~tp~n",
2465		  [Prefix,App,File]);
2466form_warn(Prefix, {obj_out_of_date,{Mod,App,_}}) ->
2467    io_lib:format("~ts~w: Object code (~w) out of date~n",
2468		  [Prefix,App,Mod]);
2469form_warn(Prefix, {exref_undef, Undef}) ->
2470    F = fun({M,F,A}) ->
2471		io_lib:format("~tsUndefined function ~w:~tw/~w~n",
2472			      [Prefix,M,F,A])
2473	end,
2474    map(F, Undef);
2475form_warn(Prefix, missing_sasl) ->
2476    io_lib:format("~tsMissing application sasl. "
2477		  "Can not upgrade with this release~n",
2478		  [Prefix]);
2479form_warn(Prefix, What) ->
2480    io_lib:format("~ts~tp~n", [Prefix,What]).
2481