From a5240467ac0b5428063360fc4a3d67ab9ffa1413 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 16 Feb 2012 14:58:57 +0100 Subject: [reltool] Link together escript with inlined application OTP-9968 Make sure that inlined applications in an escript is included/excluded as the escript itself, and forbid explicit configuration of the inlined application. --- lib/reltool/src/reltool.hrl | 3 +- lib/reltool/src/reltool_mod_win.erl | 6 +- lib/reltool/src/reltool_server.erl | 113 +++++++++++++++++++---------- lib/reltool/src/reltool_target.erl | 8 +-- lib/reltool/test/reltool_server_SUITE.erl | 116 ++++++++++++++++++++++++++++-- 5 files changed, 193 insertions(+), 53 deletions(-) diff --git a/lib/reltool/src/reltool.hrl b/lib/reltool/src/reltool.hrl index 8e80c80e10..0a90c42ce2 100644 --- a/lib/reltool/src/reltool.hrl +++ b/lib/reltool/src/reltool.hrl @@ -45,6 +45,7 @@ -type profile() :: development | embedded | standalone. -type relocatable() :: boolean(). -type escript_file() :: file(). +-type escript_app_name() :: app_name(). -type mod_name() :: atom(). -type app_name() :: atom(). -type app_vsn() :: string(). % e.g. "4.7" @@ -170,7 +171,7 @@ -record(app, { %% Static info name :: app_name(), - is_escript :: boolean(), + is_escript :: boolean() | {inlined, escript_app_name()}, use_selected_vsn :: boolean() | undefined, active_dir :: dir(), sorted_dirs :: [dir()], diff --git a/lib/reltool/src/reltool_mod_win.erl b/lib/reltool/src/reltool_mod_win.erl index e1c2fa5100..8cf175547b 100644 --- a/lib/reltool/src/reltool_mod_win.erl +++ b/lib/reltool/src/reltool_mod_win.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2011. All Rights Reserved. +%% Copyright Ericsson AB 2009-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -314,8 +314,8 @@ do_create_code_page(#state{xref_pid = Xref, mod = M} = S, PageName) -> {ok, App} = reltool_server:get_app(Xref, M#mod.app_name), ErlBin = case App#app.is_escript of - true -> find_escript_bin(App, M); - false -> find_regular_bin(App, M) + false -> find_regular_bin(App, M); + _ -> find_escript_bin(App, M) end, load_code(Editor, ErlBin), diff --git a/lib/reltool/src/reltool_server.erl b/lib/reltool/src/reltool_server.erl index da15d91581..f5abaf0957 100644 --- a/lib/reltool/src/reltool_server.erl +++ b/lib/reltool/src/reltool_server.erl @@ -467,20 +467,30 @@ loop(#state{common = C, sys = Sys} = S) -> do_set_apps(#state{sys = Sys} = S, ChangedApps, Status) -> %% Create new list of configured applications - Sys2 = Sys#sys{apps = app_update_config(ChangedApps, Sys#sys.apps)}, + {SysApps,Status2} = app_update_config(ChangedApps, Sys#sys.apps, Status), + Sys2 = Sys#sys{apps = SysApps}, - %% Refresh and analyse - {S2, Apps, Status2} = refresh(S#state{sys = Sys2}, true, Status), - Status3 = analyse(S2, Apps, Status2), + %% Refresh from filesystem and analyse dependencies + {S2, Apps, Status3} = refresh(S#state{sys = Sys2}, true, Status2), + Status4 = analyse(S2, Apps, Status3), - {S2, Status3}. + {S2, Status4}. %% Re-create the #sys.apps list by %% 1) taking configurable fields from the changed #app records and %% create new default records %% 2) removing #app records if no configurable fields are set %% 3) keeping #app records that are not changed -app_update_config([Config|Configs],SysApps) -> +app_update_config([#app{name=Name,is_escript={inlined,Escript}}|Configs], + SysApps,Status) -> + Text = + lists:flatten( + io_lib:format("Application ~p is inlined in ~p. Can not change " + "configuration for an inlined application.", + [Name,Escript])), + Status2 = reltool_utils:return_first_error(Status, Text), + app_update_config(Configs,SysApps,Status2); +app_update_config([Config|Configs],SysApps,Status) -> NewSysApps = case app_set_config_only(Config) of {delete,Name} -> @@ -488,9 +498,9 @@ app_update_config([Config|Configs],SysApps) -> New -> lists:ukeymerge(#app.name,[New],SysApps) end, - app_update_config(Configs,NewSysApps); -app_update_config([],SysApps) -> - SysApps. + app_update_config(Configs,NewSysApps,Status); +app_update_config([],SysApps,Status) -> + {SysApps,Status}. app_set_config_only(#app{mods=ConfigMods} = Config) -> app_set_config_only(mod_set_config_only(ConfigMods),Config). @@ -506,13 +516,13 @@ app_set_config_only([],#app{name = Name, excl_app_filters = undefined, incl_archive_filters = undefined, excl_archive_filters = undefined, - archive_opts = undefined}) -> + archive_opts = undefined, + is_escript = false})-> {delete,Name}; app_set_config_only(Mods,#app{name = Name, incl_cond = InclCond, mod_cond = ModCond, use_selected_vsn = UseSelectedVsn, - active_dir = ActiveDir, debug_info = DebugInfo, app_file = AppFile, app_type = AppType, @@ -521,21 +531,38 @@ app_set_config_only(Mods,#app{name = Name, incl_archive_filters = InclArchiveFilters, excl_archive_filters = ExclArchiveFilters, archive_opts = ArchiveOpts, - vsn = Vsn}) -> - (default_app(Name))#app{incl_cond = InclCond, - mod_cond = ModCond, - use_selected_vsn = UseSelectedVsn, - active_dir = ActiveDir, - debug_info = DebugInfo, - app_file = AppFile, - app_type = AppType, - incl_app_filters = InclAppFilters, - excl_app_filters = ExclAppFilters, - incl_archive_filters = InclArchiveFilters, - excl_archive_filters = ExclArchiveFilters, - archive_opts = ArchiveOpts, - vsn = Vsn, - mods = Mods}. + vsn = Vsn, + is_escript = IsEscript, + label = Label, + info = Info, + active_dir = ActiveDir, + sorted_dirs = SortedDirs}) -> + App = (default_app(Name))#app{incl_cond = InclCond, + mod_cond = ModCond, + use_selected_vsn = UseSelectedVsn, + debug_info = DebugInfo, + app_file = AppFile, + app_type = AppType, + incl_app_filters = InclAppFilters, + excl_app_filters = ExclAppFilters, + incl_archive_filters = InclArchiveFilters, + excl_archive_filters = ExclArchiveFilters, + archive_opts = ArchiveOpts, + vsn = Vsn, + mods = Mods}, + + %% Some fields shall only be set if it is an escript, e.g. label + %% must never be set for any other applications since that will + %% prevent refreshing. + if IsEscript -> + App#app{is_escript = IsEscript, + active_dir = ActiveDir, + sorted_dirs = SortedDirs, + label = Label, + info = Info}; + true -> + App + end. mod_set_config_only(ConfigMods) -> [#mod{name = Name, @@ -1044,7 +1071,7 @@ refresh_app(#app{name = AppName, %% And read all modules from ebin and create %% #mod record with dependencies (uses_mods). {AI, read_ebin_mods(Ebin, AppName), Status2}; - true -> + _ -> {App#app.info, Mods, Status} end, @@ -1316,7 +1343,7 @@ shrink_app(A) -> info = undefined, mods = [], uses_mods = undefined}; - true -> + true -> {Dir, Dirs, OptVsn} = case A#app.use_selected_vsn of undefined -> @@ -1881,7 +1908,7 @@ escripts_to_apps([Escript | Escripts], Apps, Status) -> EA -> EA end, {Apps2, Status3} = - escript_files_to_apps(Escript, + escript_files_to_apps(EscriptAppName, lists:sort(Files), [EscriptApp], Apps, @@ -1898,7 +1925,7 @@ escripts_to_apps([], Apps, Status) -> %% Assume that all files for an app are in consecutive order %% Assume the app info is before the mods -escript_files_to_apps(Escript, +escript_files_to_apps(EscriptAppName, [{AppName, Type, Dir, ModOrInfo} | Files], Acc, Apps, @@ -1914,6 +1941,7 @@ escript_files_to_apps(Escript, {[App#app{mods = Mods} | Acc2], Status}; Acc -> {NewApp, Status2} = init_escript_app(AppName, + EscriptAppName, Dir, missing_app_info(""), [ModOrInfo], @@ -1923,6 +1951,7 @@ escript_files_to_apps(Escript, end; app -> {App, Status2} = init_escript_app(AppName, + EscriptAppName, Dir, ModOrInfo, [], @@ -1930,18 +1959,24 @@ escript_files_to_apps(Escript, Status), {[App | Acc], Status2} end, - escript_files_to_apps(Escript, Files, NewAcc, Apps, Status3); -escript_files_to_apps(_Escript, [], Acc, Apps, Status) -> - {lists:ukeymerge(#app.name, Acc, Apps), Status}. + escript_files_to_apps(EscriptAppName, Files, NewAcc, Apps, Status3); +escript_files_to_apps(_EscriptAppName, [], Acc, Apps, Status) -> + {lists:ukeymerge(#app.name, lists:reverse(Acc), Apps), Status}. -init_escript_app(AppName, Dir, Info, Mods, Apps, Status) -> +init_escript_app(AppName, EscriptAppName, Dir, Info, Mods, Apps, Status) -> App1 = default_app(AppName, Dir), - App2 = App1#app{is_escript = true, + IsEscript = + if AppName=:=EscriptAppName -> true; + true -> {inlined, EscriptAppName} + end, + InclCond = (lists:keyfind(EscriptAppName,#app.name,Apps))#app.incl_cond, + App2 = App1#app{is_escript = IsEscript, label = filename:basename(Dir, ".escript"), info = Info, mods = Mods, active_dir = Dir, - sorted_dirs = [Dir]}, + sorted_dirs = [Dir], + incl_cond = InclCond},% inlined apps inherit incl from escript case lists:keymember(AppName, #app.name, Apps) of true -> Error = lists:concat([AppName, ": Application name clash. ", @@ -2022,8 +2057,10 @@ refresh_apps(_ConfigApps, [], Acc, _Force, Status) -> {lists:reverse(Acc), Status}. -ensure_app_info(#app{is_escript = true, active_dir = Dir, info = Info}, - Status) -> +ensure_app_info(#app{is_escript = IsEscript, active_dir = Dir, info = Info}, + Status) + when IsEscript=/=false -> + %% Escript or application which is inlined in an escript {Info, Dir, Status}; ensure_app_info(#app{name = Name, sorted_dirs = []}, Status) -> Error = lists:concat([Name, ": Missing application directory."]), diff --git a/lib/reltool/src/reltool_target.erl b/lib/reltool/src/reltool_target.erl index 0fcf89a360..40d1009733 100644 --- a/lib/reltool/src/reltool_target.erl +++ b/lib/reltool/src/reltool_target.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2011. All Rights Reserved. +%% Copyright Ericsson AB 2009-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -101,7 +101,7 @@ do_gen_config(#sys{root_dir = RootDir, || A <- Apps, A#app.name =/= ?MISSING_APP_NAME, A#app.name =/= erts, - not A#app.is_escript], + A#app.is_escript =/= true], EscriptItems = [{escript, A#app.active_dir, emit(incl_cond, A#app.incl_cond, undefined, InclDefs)} @@ -895,7 +895,7 @@ spec_escripts(#sys{apps = Apps}, ErtsBin, BinFiles) -> if Name =:= ?MISSING_APP_NAME -> false; - not IsEscript -> + IsEscript =/= true -> false; IsIncl; IsPre -> {true, do_spec_escript(File, ErtsBin, BinFiles)}; @@ -957,7 +957,7 @@ spec_lib_files(#sys{apps = Apps} = Sys) -> if Name =:= ?MISSING_APP_NAME -> false; - IsEscript -> + IsEscript =/= false -> false; IsIncl; IsPre -> true; diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl index f2b63f78f0..e279be82a8 100644 --- a/lib/reltool/test/reltool_server_SUITE.erl +++ b/lib/reltool/test/reltool_server_SUITE.erl @@ -64,6 +64,7 @@ all() -> create_standalone, create_standalone_beam, create_standalone_app, + create_standalone_app_clash, create_multiple_standalone, create_old_target, eval_target_spec, @@ -75,6 +76,7 @@ all() -> get_sys, set_app_and_undo, set_apps_and_undo, + set_apps_inlined, set_sys_and_undo, load_config_and_undo, load_config_escript_path, @@ -798,13 +800,6 @@ create_standalone_beam(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Generate standalone system with inlined archived application -%% BUG: see OTP-9792, 25) -%% Problem related to change of #app.name from "*escript* someapp" to -%% just "someapp", since there is a someapp.app in the -%% archive. "someapp" does not have an incl_cond=include statement, -%% and it is not derived so the it is not included in the target -%% system. -create_standalone_app(_Config) -> {skip, "Known bug: escript with inlined archive is not handled correctly by reltool"}; create_standalone_app(Config) -> %% Create archive DataDir = ?config(data_dir,Config), @@ -854,6 +849,43 @@ create_standalone_app(Config) -> ok. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Generate standalone system with inlined archived application +%% Check that the inlined app can not be explicitly configured + +create_standalone_app_clash(Config) -> + %% Create archive + DataDir = ?config(data_dir,Config), + EscriptDir = filename:join(DataDir,escript), + {ok,{_Archive,Bin}} = zip:create("someapp-1.0.ez",["someapp-1.0"], + [memory, + {cwd,EscriptDir}, + {compress,all}, + {uncompress,[".beam",".app"]}]), + + %% Create the escript + EscriptName = "someapp.escript", + Escript = filename:join(?WORK_DIR,EscriptName), + ok = escript:create(Escript,[shebang, + {emu_args,"-escript main mymod"}, + {archive,Bin}]), + ok = file:change_mode(Escript,8#00744), + + %% Configure the server + Sys = + {sys, + [ + {lib_dirs, []}, + {escript, Escript, [{incl_cond, include}]}, + {profile, standalone}, + {app, someapp, [{incl_cond,include}]} + ]}, + + ?msym({error,"someapp: Application name clash. Escript "++_}, + reltool:get_target_spec([{config,Sys}])), + + ok. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Generate standalone system with multiple escripts @@ -1266,6 +1298,76 @@ set_apps_and_undo(Config) -> ok. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Test that escript can be configured, but not its inlined applications +set_apps_inlined(Config) -> + %% Create archive + DataDir = ?config(data_dir,Config), + EscriptDir = filename:join(DataDir,escript), + {ok,{_Archive,Bin}} = zip:create("someapp-1.0.ez",["someapp-1.0"], + [memory, + {cwd,EscriptDir}, + {compress,all}, + {uncompress,[".beam",".app"]}]), + + %% Create the escript + EscriptName = "someapp.escript", + Escript = filename:join(?WORK_DIR,EscriptName), + ok = escript:create(Escript,[shebang, + {emu_args,"-escript main mymod"}, + {archive,Bin}]), + ok = file:change_mode(Escript,8#00744), + + %% Configure the server + Sys = {sys,[{incl_cond, exclude}, + {escript,Escript,[]}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + ?msym({ok,[]},reltool_server:get_status(Pid)), + + %% Get app and mod + {ok,EApp} = ?msym({ok,_}, reltool_server:get_app(Pid,'*escript* someapp')), + {ok,Someapp} = ?msym({ok,_}, reltool_server:get_app(Pid,someapp)), + ?m(undefined, EApp#app.incl_cond), + ?m(undefined, Someapp#app.incl_cond), + ?m(false, Someapp#app.is_included), + ?m(false, Someapp#app.is_pre_included), + + %% Include escript + EApp1 = EApp#app{incl_cond=include}, + ?m({ok,[]}, reltool_server:set_apps(Pid,[EApp1])), + ExpectedEApp = EApp1#app{is_included=true,is_pre_included=true}, + ?m({ok,ExpectedEApp}, reltool_server:get_app(Pid,'*escript* someapp')), + {ok,Someapp1} = ?msym({ok,_}, reltool_server:get_app(Pid,someapp)), + ?m(include, Someapp1#app.incl_cond), + ?m(true, Someapp1#app.is_included), + ?m(true, Someapp1#app.is_pre_included), + + %% Check that inlined app can not be configured + Someapp2 = Someapp1#app{incl_cond=exclude}, + ?msym({error, + "Application someapp is inlined in '*escript* someapp'. " + "Can not change configuration for an inlined application."}, + reltool_server:set_apps(Pid,[Someapp2])), + ?m({ok,Someapp1}, reltool_server:get_app(Pid,someapp)), + + %% Exclude escript + {ok,EApp2} = ?msym({ok,_}, reltool_server:get_app(Pid,'*escript* someapp')), + EApp3 = EApp2#app{incl_cond=exclude}, + ?m({ok,[]}, reltool_server:set_apps(Pid,[EApp3])), + ExpectedEApp3 = EApp3#app{is_included=false,is_pre_included=false}, + ?m({ok,ExpectedEApp3}, reltool_server:get_app(Pid,'*escript* someapp')), + {ok,Someapp3} = ?msym({ok,_}, reltool_server:get_app(Pid,someapp)), + ?m(exclude, Someapp3#app.incl_cond), + ?m(false, Someapp3#app.is_included), + ?m(false, Someapp3#app.is_pre_included), + + ?m(ok, reltool:stop(Pid)), + ok. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% set_sys_and_undo(Config) -> Sys1 = {sys,[{incl_cond, exclude}, -- cgit v1.2.3