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