From 40014d4b190c86044df5f1aa70dad7138d531b7b Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 4 Jan 2012 16:09:01 +0100 Subject: [reltool] Add more test cases and fix bugs OTP-9794 The following test cases are added for the inteface from GUI to reltool_server: * get_config * get_apps * set_app_and_undo * set_apps_and_undo * load_config_and_undo * reset_config_and_undo * save_config The following bugs were found and corrected: * If set_apps failed, then the state of reltool_server would not be reset to how it was before the failing operation - and every operation done afterwards would also (seem to) fail. * undo_config did not work after reset_config - since faulty #sys record was stored as old_sys. * undo_config did not work after set_app (used when changing the content of an application from the GUI) - since old_sys was not set. Also old_status was not set causing possible warnings to disappear. * undo_config did not work after set_apps (used e.g. when excluding or including an application from the GUI) - since old_sys was not set. Also old_status was not set causing possible warnings to disappear. --- lib/reltool/src/reltool.hrl | 6 +- lib/reltool/src/reltool_server.erl | 59 ++-- lib/reltool/test/reltool_server_SUITE.erl | 391 ++++++++++++++++++++- .../faulty_app_file/a-1.0/ebin/a.app | 1 + .../faulty_app_file/a-1.0/src/a.erl | 49 +++ .../faulty_app_file/a-1.0/src/a_sup.erl | 37 ++ 6 files changed, 509 insertions(+), 34 deletions(-) create mode 100644 lib/reltool/test/reltool_server_SUITE_data/faulty_app_file/a-1.0/ebin/a.app create mode 100644 lib/reltool/test/reltool_server_SUITE_data/faulty_app_file/a-1.0/src/a.erl create mode 100644 lib/reltool/test/reltool_server_SUITE_data/faulty_app_file/a-1.0/src/a_sup.erl (limited to 'lib') diff --git a/lib/reltool/src/reltool.hrl b/lib/reltool/src/reltool.hrl index 93f47f6381..8e80c80e10 100644 --- a/lib/reltool/src/reltool.hrl +++ b/lib/reltool/src/reltool.hrl @@ -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 @@ -199,8 +199,8 @@ used_by_mods :: [mod_name()], uses_apps :: [app_name()], used_by_apps :: [app_name()], - is_pre_included :: boolean(), - is_included :: boolean(), + is_pre_included :: boolean() | undefined, + is_included :: boolean() | undefined, rels :: [rel_name()] }). diff --git a/lib/reltool/src/reltool_server.erl b/lib/reltool/src/reltool_server.erl index 692baea0a4..fdb9e08a90 100644 --- a/lib/reltool/src/reltool_server.erl +++ b/lib/reltool/src/reltool_server.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 @@ -254,7 +254,7 @@ loop(#state{common = C, sys = Sys} = S) -> {S2, Status} = parse_options(S#state.options), S3 = shrink_sys(S2), {S4, Status2} = refresh(S3, true, Status), - {S5, Status3} = analyse(S4#state{old_sys = S4#state.sys}, Status2), + {S5, Status3} = analyse(S4#state{old_sys = S#state.sys}, Status2), S6 = case Status3 of {ok, _Warnings} -> @@ -266,11 +266,11 @@ loop(#state{common = C, sys = Sys} = S) -> reltool_utils:reply(ReplyTo, Ref, Status3), ?MODULE:loop(S6); {call, ReplyTo, Ref, undo_config} -> - reltool_utils:reply(ReplyTo, Ref, ok), S2 = S#state{sys = S#state.old_sys, old_sys = S#state.sys, status = S#state.old_status, old_status = S#state.status}, + reltool_utils:reply(ReplyTo, Ref, ok), ?MODULE:loop(S2); {call, ReplyTo, Ref, {get_rel, RelName}} -> Sys = S#state.sys, @@ -319,19 +319,21 @@ loop(#state{common = C, sys = Sys} = S) -> ?MODULE:loop(S); {call, ReplyTo, Ref, {set_app, App}} -> {S2, Status} = do_set_app(S, App, {ok, []}), - {S3, Status2} = analyse(S2, Status), - case Status2 of - {ok, Warnings} -> - App2 = ?KEYSEARCH(App#app.name, - #app.name, - (S3#state.sys)#sys.apps), - reltool_utils:reply(ReplyTo, Ref, {ok, App2, Warnings}), - ?MODULE:loop(S3); - {error, Reason} -> - %% Keep old state - reltool_utils:reply(ReplyTo, Ref, {error, Reason}), - ?MODULE:loop(S) - end; + {S3, Status2} = analyse(S2#state{old_sys=S#state.sys}, Status), + {S4, Reply} = + case Status2 of + {ok, Warnings} -> + App2 = ?KEYSEARCH(App#app.name, + #app.name, + (S3#state.sys)#sys.apps), + {S3#state{status=Status2, old_status=S#state.status}, + {ok, App2, Warnings}}; + {error, _} -> + %% Keep old state + {S, Status2} + end, + reltool_utils:reply(ReplyTo, Ref, Reply), + ?MODULE:loop(S4); {call, ReplyTo, Ref, {get_apps, Kind}} -> AppNames = case Kind of @@ -361,9 +363,17 @@ loop(#state{common = C, sys = Sys} = S) -> lists:foldl(fun(A, {X, Y}) -> do_set_app(X, A, Y) end, {S, {ok, []}}, Apps), - {S3, Status2} = analyse(S2, Status), + {S3, Status2} = analyse(S2#state{old_sys = S#state.sys}, Status), + S4 = + case Status2 of + {ok, _Warnings} -> + S3#state{status=Status2, old_status=S#state.status}; + {error, _} -> + %% Keep old state + S + end, reltool_utils:reply(ReplyTo, Ref, Status2), - ?MODULE:loop(S3); + ?MODULE:loop(S4); {call, ReplyTo, Ref, get_sys} -> reltool_utils:reply(ReplyTo, Ref, {ok, Sys#sys{apps = undefined}}), ?MODULE:loop(S); @@ -374,19 +384,16 @@ loop(#state{common = C, sys = Sys} = S) -> (Sys2#sys.lib_dirs =/= Sys#sys.lib_dirs) orelse (Sys2#sys.escripts =/= Sys#sys.escripts), {S3, Status} = refresh(S2, Force, {ok, []}), - {S4, Status2} = - analyse(S3#state{old_sys = S#state.sys}, Status), - {S5, Status3} = + {S4, Status2} = analyse(S3#state{old_sys = S#state.sys}, Status), + S5 = case Status2 of {ok, _Warnings} -> % BUGBUG: handle warnings - {S4#state{status = Status2, - old_status = S#state.status}, - Status2}; + S4#state{status = Status2, old_status = S#state.status}; {error, _} -> %% Keep old state - {S, Status2} + S end, - reltool_utils:reply(ReplyTo, Ref, Status3), + reltool_utils:reply(ReplyTo, Ref, Status2), ?MODULE:loop(S5); {call, ReplyTo, Ref, get_status} -> reltool_utils:reply(ReplyTo, Ref, S#state.status), diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl index 9ed79e8c95..588db837e4 100644 --- a/lib/reltool/test/reltool_server_SUITE.erl +++ b/lib/reltool/test/reltool_server_SUITE.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 @@ -24,6 +24,7 @@ -compile(export_all). +-include_lib("reltool/src/reltool.hrl"). -include("reltool_test_lib.hrl"). -include_lib("common_test/include/ct.hrl"). @@ -51,10 +52,12 @@ end_per_testcase(Func,Config) -> suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [start_server, set_config, create_release, + [start_server, set_config, get_config, create_release, create_script, create_target, create_embedded, create_standalone, create_old_target, - otp_9135, otp_9229_exclude_app, otp_9229_exclude_mod]. + otp_9135, otp_9229_exclude_app, otp_9229_exclude_mod, + get_apps, set_app_and_undo, set_apps_and_undo, + load_config_and_undo, reset_config_and_undo, save_config]. groups() -> []. @@ -111,6 +114,83 @@ set_config(_Config) -> ?m(ok, reltool:stop(Pid)), ok. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Check that get_config returns the expected derivates and defaults +%% as specified +get_config(_Config) -> + Sys = {sys,[{incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + ?m({ok, Sys}, reltool:get_config(Pid)), + ?m({ok, Sys}, reltool:get_config(Pid,false,false)), + + ?msym({ok,{sys,[{incl_cond, exclude}, + {erts,[]}, + {app,kernel,[{incl_cond,include},{mod,_,[]}|_]}, + {app,sasl,[{incl_cond,include},{mod,_,[]}|_]}, + {app,stdlib,[{incl_cond,include},{mod,_,[]}|_]}]}}, + reltool:get_config(Pid,false,true)), + + ?msym({ok,{sys,[{root_dir,_}, + {lib_dirs,_}, + {mod_cond,all}, + {incl_cond,exclude}, + {app,kernel,[{incl_cond,include},{vsn,undefined}]}, + {app,sasl,[{incl_cond,include},{vsn,undefined}]}, + {app,stdlib,[{incl_cond,include},{vsn,undefined}]}, + {boot_rel,"start_clean"}, + {rel,"start_clean","1.0",[]}, + {rel,"start_sasl","1.0",[sasl]}, + {emu_name,"beam"}, + {relocatable,true}, + {profile,development}, + {incl_sys_filters,[".*"]}, + {excl_sys_filters,[]}, + {incl_app_filters,[".*"]}, + {excl_app_filters,[]}, + {incl_archive_filters,[".*"]}, + {excl_archive_filters,["^include$","^priv$"]}, + {archive_opts,[]}, + {rel_app_type,permanent}, + {app_file,keep}, + {debug_info,keep}]}}, + reltool:get_config(Pid,true,false)), + + KVsn = latest(kernel), + StdVsn = latest(stdlib), + + ?msym({ok,{sys,[{root_dir,_}, + {lib_dirs,_}, + {mod_cond,all}, + {incl_cond,exclude}, + {erts,[]}, + {app,kernel,[{incl_cond,include},{vsn,KVsn},{mod,_,[]}|_]}, + {app,sasl,[{incl_cond,include},{vsn,_},{mod,_,[]}|_]}, + {app,stdlib,[{incl_cond,include},{vsn,StdVsn},{mod,_,[]}|_]}, + {boot_rel,"start_clean"}, + {rel,"start_clean","1.0",[]}, + {rel,"start_sasl","1.0",[sasl]}, + {emu_name,"beam"}, + {relocatable,true}, + {profile,development}, + {incl_sys_filters,[".*"]}, + {excl_sys_filters,[]}, + {incl_app_filters,[".*"]}, + {excl_app_filters,[]}, + {incl_archive_filters,[".*"]}, + {excl_archive_filters,["^include$","^priv$"]}, + {archive_opts,[]}, + {rel_app_type,permanent}, + {app_file,keep}, + {debug_info,keep}]}}, + reltool:get_config(Pid,true,true)), + + ?m(ok, reltool:stop(Pid)), + ok. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% OTP-9135, test that app_file option can be set to all | keep | strip @@ -389,7 +469,7 @@ otp_9229_exclude_app(Config) -> ?m(ok, reltool_utils:recursive_delete(TargetDir)), ?m(ok, file:make_dir(TargetDir)), ?log("SPEC: ~p\n", [reltool:get_target_spec([{config, ExclApp}])]), - {ok,["Module mylib exists in applications x and y. Using module from application x."]} = reltool:get_status([{config, ExclApp}]), + ?m({ok,["Module mylib exists in applications x and y. Using module from application x."]}, reltool:get_status([{config, ExclApp}])), ?m(ok, reltool:create_target([{config, ExclApp}], TargetDir)), Erl = filename:join([TargetDir, "bin", "erl"]), @@ -436,7 +516,7 @@ otp_9229_exclude_mod(Config) -> ?m(ok, reltool_utils:recursive_delete(TargetDir)), ?m(ok, file:make_dir(TargetDir)), ?log("SPEC: ~p\n", [reltool:get_target_spec([{config, ExclMod}])]), - {ok,["Module mylib exists in applications x and y. Using module from application x."]} = reltool:get_status([{config, ExclMod}]), + ?m({ok,["Module mylib exists in applications x and y. Using module from application x."]}, reltool:get_status([{config, ExclMod}])), ?m(ok, reltool:create_target([{config, ExclMod}], TargetDir)), Erl = filename:join([TargetDir, "bin", "erl"]), @@ -466,7 +546,296 @@ otp_9229_exclude_mod(Config) -> ok. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Test the interface used by the GUI: +%% get_app +%% get_apps +%% set_app +%% set_apps +%% load_config +%% reset_config +%% +%% Also, for each operation which manipulates the config test +%% undo_config - that it is properly undone, and that warnings are +%% re-displayed. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +get_apps(_Config) -> + Sys = {sys,[{app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,tools,[{incl_cond,derived}]}, + {app,runtime_tools,[{incl_cond,exclude}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + + {ok,Sasl} = ?msym({ok,#app{name=sasl}}, reltool_server:get_app(Pid,sasl)), + {ok,[#app{name=kernel}, + #app{name=sasl}=Sasl, + #app{name=stdlib}] = White} = + ?msym({ok,_}, reltool_server:get_apps(Pid,whitelist)), + {ok,[#app{name=runtime_tools}] = Black} = + ?msym({ok,_}, reltool_server:get_apps(Pid,blacklist)), + + {ok,Derived} = ?msym({ok,_}, reltool_server:get_apps(Pid,derived)), + true = lists:keymember(tools,#app.name,Derived), + + {ok,Source} = ?msym({ok,_}, reltool_server:get_apps(Pid,source)), + true = lists:keymember(common_test,#app.name,Source), + + %% Check that the four lists are disjoint + Number = length(White) + length(Black) + length(Derived) + length(Source), + WN = lists:usort([N || #app{name=N} <- White]), + BN = lists:usort([N || #app{name=N} <- Black]), + DN = lists:usort([N || #app{name=N} <- Derived]), + SN = lists:usort([N || #app{name=N} <- Source]), + AllN = lists:umerge([WN,BN,DN,SN]), + ?m(Number,length(AllN)), + + ?m(ok, reltool:stop(Pid)), + + ok. + +set_app_and_undo(Config) -> + Sys = {sys,[{lib_dirs,[filename:join(datadir(Config),"faulty_app_file")]}, + {incl_cond, exclude}, + {app,a,[{incl_cond,include}]}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,tools,[{incl_cond,include}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + ?m({ok, Sys}, reltool:get_config(Pid)), + + %% Exclude one module with set_app + {ok,Tools} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), + Mods = Tools#app.mods, + Cover = lists:keyfind(cover,#mod.name,Mods), + ExclCover = Cover#mod{incl_cond=exclude}, + Tools1 = Tools#app{mods = lists:keyreplace(cover,#mod.name,Mods,ExclCover)}, + {ok,ToolsNoCover,[]} = ?msym({ok,_,[]}, reltool_server:set_app(Pid,Tools1)), + ?m({ok,ToolsNoCover}, reltool_server:get_app(Pid,tools)), + + %% Undo + ?m(ok, reltool_server:undo_config(Pid)), + ?m({ok,Tools}, reltool_server:get_app(Pid,tools)), + %%! warning can come twice here... :( + ?msym({ok,["a: Cannot parse app file"++_|_]}, reltool:get_status(Pid)), + + %% Undo again, to check that it toggles + ?m(ok, reltool_server:undo_config(Pid)), + ?m({ok,ToolsNoCover}, reltool_server:get_app(Pid,tools)), + ?m({ok,[]}, reltool:get_status(Pid)), + + ?m(ok, reltool:stop(Pid)), + ok. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +set_apps_and_undo(Config) -> + Sys = {sys,[{lib_dirs,[filename:join(datadir(Config),"faulty_app_file")]}, + {incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,tools,[{incl_cond,include}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + ?m({ok, Sys}, reltool:get_config(Pid)), + + %% Exclude one application with set_apps + {ok,Tools} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), + ExclTools = Tools#app{incl_cond=exclude}, + ?m({ok,[]}, reltool_server:set_apps(Pid,[ExclTools])), + {ok,NoTools} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), + ?m(false, NoTools#app.is_pre_included), + ?m(false, NoTools#app.is_included), + + %% Undo + ?m(ok, reltool_server:undo_config(Pid)), + ?m({ok,Tools}, reltool_server:get_app(Pid,tools)), + %%! warning can come twice here... :( + ?msym({ok,["a: Cannot parse app file"++_|_]}, reltool:get_status(Pid)), + + %% Undo again, to check that it toggles + ?m(ok, reltool_server:undo_config(Pid)), + ?m({ok,NoTools}, reltool_server:get_app(Pid,tools)), + ?m({ok,[]}, reltool:get_status(Pid)), + + ?m(ok, reltool:stop(Pid)), + ok. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +load_config_and_undo(Config) -> + Sys1 = {sys,[{incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,tools,[{incl_cond,include}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys1}])), + ?m({ok, Sys1}, reltool:get_config(Pid)), + + %% Check that tools is included + {ok,Tools1} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), + ?m(true, Tools1#app.is_pre_included), + ?m(true, Tools1#app.is_included), + + %% Exclude one application with set_apps + Sys2 = {sys,[{lib_dirs,[filename:join(datadir(Config),"faulty_app_file")]}, + {incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,tools,[{incl_cond,derived}]}, + {app,a,[{incl_cond,include}]}]}, + ?msym({ok,["a: Cannot parse app file"++_]}, + reltool_server:load_config(Pid,Sys2)), + + %% Check that tools is included (since it is used by sasl) but not + %% pre-included (neither included or excluded => undefined) + {ok,Tools2} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), + ?m(undefined, Tools2#app.is_pre_included), + ?m(true, Tools2#app.is_included), + + %% Undo + ?m(ok, reltool_server:undo_config(Pid)), + ?m({ok,Tools1}, reltool_server:get_app(Pid,tools)), + ?m({ok,[]}, reltool:get_status(Pid)), + + %% Undo again, to check that it toggles + ?m(ok, reltool_server:undo_config(Pid)), + ?m({ok,Tools2}, reltool_server:get_app(Pid,tools)), + ?msym({ok,["a: Cannot parse app file"++_]}, reltool:get_status(Pid)), + ?m(ok, reltool:stop(Pid)), + ok. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +reset_config_and_undo(Config) -> + Sys1 = {sys,[{lib_dirs,[filename:join(datadir(Config),"faulty_app_file")]}, + {incl_cond, exclude}, + {app,a,[{incl_cond,include}]}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,tools,[{incl_cond,include}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys1}])), + ?m({ok, Sys1}, reltool:get_config(Pid)), + + %% Check that tools is included + {ok,Tools1} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), + ?m(true, Tools1#app.is_pre_included), + ?m(true, Tools1#app.is_included), + + %% Exclude one application with set_apps + Sys2 = {sys,[{incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,tools,[{incl_cond,exclude}]}]}, + ?m({ok,[]}, reltool_server:load_config(Pid,Sys2)), + + %% Check that tools is excluded + {ok,Tools2} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), + ?m(false, Tools2#app.is_pre_included), + ?m(false, Tools2#app.is_included), + + %% Reset + %%! warning can come twice here... :( + ?msym({ok,["a: Cannot parse app file"++_|_]}, + reltool_server:reset_config(Pid)), + ?m({ok,Tools1}, reltool_server:get_app(Pid,tools)), + + %% Undo again, to check that it toggles + ?m(ok, reltool_server:undo_config(Pid)), + ?m({ok,Tools2}, reltool_server:get_app(Pid,tools)), + ?m({ok,[]}, reltool:get_status(Pid)), + + ?m(ok, reltool:stop(Pid)), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +save_config(Config) -> + PrivDir = ?config(priv_dir,Config), + Sys = {sys,[{incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + ?m({ok, Sys}, reltool:get_config(Pid)), + + Simple = filename:join(PrivDir,"save_simple.reltool"), + ?m(ok, reltool_server:save_config(Pid,Simple,false,false)), + ?m({ok,[Sys]}, file:consult(Simple)), + + Derivates = filename:join(PrivDir,"save_derivates.reltool"), + ?m(ok, reltool_server:save_config(Pid,Derivates,false,true)), + ?msym({ok,[{sys,[{incl_cond, exclude}, + {erts,[]}, + {app,kernel,[{incl_cond,include},{mod,_,[]}|_]}, + {app,sasl,[{incl_cond,include},{mod,_,[]}|_]}, + {app,stdlib,[{incl_cond,include},{mod,_,[]}|_]}]}]}, + file:consult(Derivates)), + + Defaults = filename:join(PrivDir,"save_defaults.reltool"), + ?m(ok, reltool_server:save_config(Pid,Defaults,true,false)), + ?msym({ok,[{sys,[{root_dir,_}, + {lib_dirs,_}, + {mod_cond,all}, + {incl_cond,exclude}, + {app,kernel,[{incl_cond,include},{vsn,undefined}]}, + {app,sasl,[{incl_cond,include},{vsn,undefined}]}, + {app,stdlib,[{incl_cond,include},{vsn,undefined}]}, + {boot_rel,"start_clean"}, + {rel,"start_clean","1.0",[]}, + {rel,"start_sasl","1.0",[sasl]}, + {emu_name,"beam"}, + {relocatable,true}, + {profile,development}, + {incl_sys_filters,[".*"]}, + {excl_sys_filters,[]}, + {incl_app_filters,[".*"]}, + {excl_app_filters,[]}, + {incl_archive_filters,[".*"]}, + {excl_archive_filters,["^include$","^priv$"]}, + {archive_opts,[]}, + {rel_app_type,permanent}, + {app_file,keep}, + {debug_info,keep}]}]}, + file:consult(Defaults)), + + KVsn = latest(kernel), + StdVsn = latest(stdlib), + + All = filename:join(PrivDir,"save_all.reltool"), + ?m(ok, reltool_server:save_config(Pid,All,true,true)), + ?msym({ok,[{sys,[{root_dir,_}, + {lib_dirs,_}, + {mod_cond,all}, + {incl_cond,exclude}, + {erts,[]}, + {app,kernel,[{incl_cond,include},{vsn,KVsn},{mod,_,[]}|_]}, + {app,sasl,[{incl_cond,include},{vsn,_},{mod,_,[]}|_]}, + {app,stdlib,[{incl_cond,include},{vsn,StdVsn},{mod,_,[]}|_]}, + {boot_rel,"start_clean"}, + {rel,"start_clean","1.0",[]}, + {rel,"start_sasl","1.0",[sasl]}, + {emu_name,"beam"}, + {relocatable,true}, + {profile,development}, + {incl_sys_filters,[".*"]}, + {excl_sys_filters,[]}, + {incl_app_filters,[".*"]}, + {excl_app_filters,[]}, + {incl_archive_filters,[".*"]}, + {excl_archive_filters,["^include$","^priv$"]}, + {archive_opts,[]}, + {rel_app_type,permanent}, + {app_file,keep}, + {debug_info,keep}]}]}, + file:consult(All)), + + ?m(ok, reltool:stop(Pid)), + ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -478,6 +847,18 @@ erl_libs() -> LibStr -> string:tokens(LibStr, ":;") end. +datadir(Config) -> + %% Removes the trailing slash... + filename:nativename(?config(data_dir,Config)). + +latest(App) -> + AppStr = atom_to_list(App), + AppDirs = filelib:wildcard(filename:join(code:lib_dir(),AppStr++"-*")), + [LatestAppDir|_] = lists:reverse(AppDirs), + [_,Vsn] = string:tokens(filename:basename(LatestAppDir),"-"), + Vsn. + + diff_script(Script, Script) -> equal; diff_script({script, Rel, Commands1}, {script, Rel, Commands2}) -> diff --git a/lib/reltool/test/reltool_server_SUITE_data/faulty_app_file/a-1.0/ebin/a.app b/lib/reltool/test/reltool_server_SUITE_data/faulty_app_file/a-1.0/ebin/a.app new file mode 100644 index 0000000000..ea77103598 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/faulty_app_file/a-1.0/ebin/a.app @@ -0,0 +1 @@ +faulty app file diff --git a/lib/reltool/test/reltool_server_SUITE_data/faulty_app_file/a-1.0/src/a.erl b/lib/reltool/test/reltool_server_SUITE_data/faulty_app_file/a-1.0/src/a.erl new file mode 100644 index 0000000000..bb500bed69 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/faulty_app_file/a-1.0/src/a.erl @@ -0,0 +1,49 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(a). + + +-behaviour(gen_server). + +-vsn(1). + +%% External exports +-export([start_link/0, a/0]). +%% Internal exports +-export([init/1, handle_call/3, handle_info/2, terminate/2]). + +start_link() -> gen_server:start_link({local, aa}, a, [], []). + +a() -> gen_server:call(aa, a). + +%%----------------------------------------------------------------- +%% Callback functions from gen_server +%%----------------------------------------------------------------- +init([]) -> + process_flag(trap_exit, true), + {ok, state}. + +handle_call(a, _From, State) -> + X = application:get_all_env(a), + {reply, X, State}. + +handle_info(_, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. diff --git a/lib/reltool/test/reltool_server_SUITE_data/faulty_app_file/a-1.0/src/a_sup.erl b/lib/reltool/test/reltool_server_SUITE_data/faulty_app_file/a-1.0/src/a_sup.erl new file mode 100644 index 0000000000..a141c1767b --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/faulty_app_file/a-1.0/src/a_sup.erl @@ -0,0 +1,37 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(a_sup). + + +-behaviour(supervisor). + +%% External exports +-export([start/2]). + +%% Internal exports +-export([init/1]). + +start(_, _) -> + supervisor:start_link({local, a_sup}, a_sup, []). + +init([]) -> + SupFlags = {one_for_one, 4, 3600}, + Config = {a, + {a, start_link, []}, + permanent, 2000, worker, [a]}, + {ok, {SupFlags, [Config]}}. -- cgit v1.2.3 From e610c1e38219b7038f2e42b233ef226508535a90 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 9 Jan 2012 15:42:29 +0100 Subject: [reltool] Add test of dependencies derived through calls to external apps OTP-9794 This is a test of the mechanism with which reltool_server decides which applications to include and not, based on function calls between applications. This is also the mechanism which forms the base for the application- and module dependency graphs. --- lib/reltool/test/reltool_server_SUITE.erl | 142 ++++++++++++++++++++- .../test/reltool_server_SUITE_data/Makefile.src | 23 +++- .../dependencies/x-1.0/ebin/x.app | 7 + .../dependencies/x-1.0/src/x1.erl | 5 + .../dependencies/x-1.0/src/x2.erl | 5 + .../dependencies/x-1.0/src/x3.erl | 5 + .../dependencies/y-1.0/ebin/y.app | 7 + .../dependencies/y-1.0/src/y1.erl | 5 + .../dependencies/y-1.0/src/y2.erl | 5 + .../dependencies/z-1.0/ebin/z.app | 7 + .../dependencies/z-1.0/src/z1.erl | 5 + 11 files changed, 214 insertions(+), 2 deletions(-) create mode 100644 lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/ebin/x.app create mode 100644 lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/src/x1.erl create mode 100644 lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/src/x2.erl create mode 100644 lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/src/x3.erl create mode 100644 lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/ebin/y.app create mode 100644 lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/src/y1.erl create mode 100644 lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/src/y2.erl create mode 100644 lib/reltool/test/reltool_server_SUITE_data/dependencies/z-1.0/ebin/z.app create mode 100644 lib/reltool/test/reltool_server_SUITE_data/dependencies/z-1.0/src/z1.erl (limited to 'lib') diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl index 588db837e4..90288020f1 100644 --- a/lib/reltool/test/reltool_server_SUITE.erl +++ b/lib/reltool/test/reltool_server_SUITE.erl @@ -57,7 +57,8 @@ all() -> create_standalone, create_old_target, otp_9135, otp_9229_exclude_app, otp_9229_exclude_mod, get_apps, set_app_and_undo, set_apps_and_undo, - load_config_and_undo, reset_config_and_undo, save_config]. + load_config_and_undo, reset_config_and_undo, save_config, + dependencies]. groups() -> []. @@ -837,6 +838,143 @@ save_config(Config) -> ?m(ok, reltool:stop(Pid)), ok. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Test calculation of dependencies +%% The following test applications are used +%% +%% x-1.0: x1.erl x2.erl x3.erl +%% \ / (x2 calls y1, x3 calls y2) +%% y-1.0: y1.erl y2.erl +%% \ (y1 calls z1) +%% z-1.0 z1.erl +%% +%% Test includes x and derives y and z. +%% +dependencies(Config) -> + PrivDir = ?config(priv_dir,Config), + + %% Default: all modules included => y and z are included (derived) + Sys = {sys,[{lib_dirs,[filename:join(datadir(Config),"dependencies")]}, + {incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,x,[{incl_cond,include}]}, + {app,y,[{incl_cond,derived}]}, + {app,z,[{incl_cond,derived}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + + ?msym({ok,[#app{name=kernel}, + #app{name=sasl}, + #app{name=stdlib}, + #app{name=x,uses_apps=[y]}]}, + reltool_server:get_apps(Pid,whitelist)), + {ok, Der} = ?msym({ok,_}, + reltool_server:get_apps(Pid,derived)), + ?msym([#app{name=y,uses_apps=[z]}, + #app{name=z}], + rm_missing_app(Der)), + ?msym({ok,[]}, + reltool_server:get_apps(Pid,source)), + + %% Excluding x2 => y still included since y2 is used by x3 + %% z still included since z1 is used by y1 + Sys2 = {sys,[{lib_dirs,[filename:join(datadir(Config),"dependencies")]}, + {incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,x,[{incl_cond,include},{mod,x2,[{incl_cond,exclude}]}]}, + {app,y,[{incl_cond,derived}]}, + {app,z,[{incl_cond,derived}]}]}, + ?m({ok,[]}, reltool_server:load_config(Pid,Sys2)), + ?msym({ok,[#app{name=kernel}, + #app{name=sasl}, + #app{name=stdlib}, + #app{name=x,uses_apps=[y]}]}, + reltool_server:get_apps(Pid,whitelist)), + {ok, Der2} = ?msym({ok,_}, + reltool_server:get_apps(Pid,derived)), + ?msym([#app{name=y,uses_apps=[z]}, + #app{name=z}], + rm_missing_app(Der2)), + ?msym({ok,[]}, + reltool_server:get_apps(Pid,source)), + + %% Excluding x3 => y still included since y1 is used by x2 + %% z still included since z1 is used by y1 + Sys3 = {sys,[{lib_dirs,[filename:join(datadir(Config),"dependencies")]}, + {incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,x,[{incl_cond,include},{mod,x3,[{incl_cond,exclude}]}]}, + {app,y,[{incl_cond,derived}]}, + {app,z,[{incl_cond,derived}]}]}, + ?m({ok,[]}, reltool_server:load_config(Pid,Sys3)), + ?msym({ok,[#app{name=kernel}, + #app{name=sasl}, + #app{name=stdlib}, + #app{name=x,uses_apps=[y]}]}, + reltool_server:get_apps(Pid,whitelist)), + {ok, Der3} = ?msym({ok,_}, + reltool_server:get_apps(Pid,derived)), + ?msym([#app{name=y,uses_apps=[z]}, + #app{name=z}], + rm_missing_app(Der3)), + ?msym({ok,[]}, + reltool_server:get_apps(Pid,source)), + + %% Excluding x2 and x3 => y and z excluded + Sys4 = {sys,[{lib_dirs,[filename:join(datadir(Config),"dependencies")]}, + {incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,x,[{incl_cond,include}, + {mod,x2,[{incl_cond,exclude}]}, + {mod,x3,[{incl_cond,exclude}]}]}, + {app,y,[{incl_cond,derived}]}, + {app,z,[{incl_cond,derived}]}]}, + ?m({ok,[]}, reltool_server:load_config(Pid,Sys4)), + ?msym({ok,[#app{name=kernel}, + #app{name=sasl}, + #app{name=stdlib}, + #app{name=x,uses_apps=[]}]}, + reltool_server:get_apps(Pid,whitelist)), + {ok, Der4} = ?msym({ok,_}, + reltool_server:get_apps(Pid,derived)), + ?msym([], rm_missing_app(Der4)), + ?msym({ok,[#app{name=y}, + #app{name=z}]}, + reltool_server:get_apps(Pid,source)), + + %% Excluding y1 => y still included since y2 is used by x3 + %% z excluded since not used by any other than y1 + Sys5 = {sys,[{lib_dirs,[filename:join(datadir(Config),"dependencies")]}, + {incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,x,[{incl_cond,include}]}, + {app,y,[{incl_cond,derived}, + {mod,y1,[{incl_cond,exclude}]}]}, + {app,z,[{incl_cond,derived}]}]}, + ?m({ok,[]}, reltool_server:load_config(Pid,Sys5)), + ?msym({ok,[#app{name=kernel}, + #app{name=sasl}, + #app{name=stdlib}, + #app{name=x,uses_apps=[y]}]}, + reltool_server:get_apps(Pid,whitelist)), + {ok, Der5} = ?msym({ok,_}, + reltool_server:get_apps(Pid,derived)), + ?msym([#app{name=y,uses_apps=[]}], rm_missing_app(Der5)), + ?msym({ok,[#app{name=z}]}, + reltool_server:get_apps(Pid,source)), + + ?m(ok, reltool:stop(Pid)), + ok. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Library functions @@ -858,6 +996,8 @@ latest(App) -> [_,Vsn] = string:tokens(filename:basename(LatestAppDir),"-"), Vsn. +rm_missing_app(Apps) -> + lists:keydelete(?MISSING_APP_NAME,#app.name,Apps). diff_script(Script, Script) -> equal; diff --git a/lib/reltool/test/reltool_server_SUITE_data/Makefile.src b/lib/reltool/test/reltool_server_SUITE_data/Makefile.src index 049e8dd6cc..35a239bfe2 100644 --- a/lib/reltool/test/reltool_server_SUITE_data/Makefile.src +++ b/lib/reltool/test/reltool_server_SUITE_data/Makefile.src @@ -6,8 +6,16 @@ OTP9229= \ otp_9229/y-1.0/ebin/y.@EMULATOR@ \ otp_9229/y-1.0/ebin/mylib.@EMULATOR@ +DEPENDENCIES= \ + dependencies/x-1.0/ebin/x1.@EMULATOR@ \ + dependencies/x-1.0/ebin/x2.@EMULATOR@ \ + dependencies/x-1.0/ebin/x3.@EMULATOR@ \ + dependencies/y-1.0/ebin/y1.@EMULATOR@ \ + dependencies/y-1.0/ebin/y2.@EMULATOR@ \ + dependencies/z-1.0/ebin/z1.@EMULATOR@ -all: $(OTP9229) + +all: $(OTP9229) $(DEPENDENCIES) otp_9229/x-1.0/ebin/x.@EMULATOR@: otp_9229/x-1.0/src/x.erl erlc $(EFLAGS) -ootp_9229/x-1.0/ebin otp_9229/x-1.0/src/x.erl @@ -17,3 +25,16 @@ otp_9229/y-1.0/ebin/y.@EMULATOR@: otp_9229/y-1.0/src/y.erl erlc $(EFLAGS) -ootp_9229/y-1.0/ebin otp_9229/y-1.0/src/y.erl otp_9229/y-1.0/ebin/mylib.@EMULATOR@: otp_9229/y-1.0/src/mylib.erl erlc $(EFLAGS) -ootp_9229/y-1.0/ebin otp_9229/y-1.0/src/mylib.erl + +dependencies/x-1.0/ebin/x1.@EMULATOR@: dependencies/x-1.0/src/x1.erl + erlc $(EFLAGS) -odependencies/x-1.0/ebin dependencies/x-1.0/src/x1.erl +dependencies/x-1.0/ebin/x2.@EMULATOR@: dependencies/x-1.0/src/x2.erl + erlc $(EFLAGS) -odependencies/x-1.0/ebin dependencies/x-1.0/src/x2.erl +dependencies/x-1.0/ebin/x3.@EMULATOR@: dependencies/x-1.0/src/x3.erl + erlc $(EFLAGS) -odependencies/x-1.0/ebin dependencies/x-1.0/src/x3.erl +dependencies/y-1.0/ebin/y1.@EMULATOR@: dependencies/y-1.0/src/y1.erl + erlc $(EFLAGS) -odependencies/y-1.0/ebin dependencies/y-1.0/src/y1.erl +dependencies/y-1.0/ebin/y2.@EMULATOR@: dependencies/y-1.0/src/y2.erl + erlc $(EFLAGS) -odependencies/y-1.0/ebin dependencies/y-1.0/src/y2.erl +dependencies/z-1.0/ebin/z1.@EMULATOR@: dependencies/z-1.0/src/z1.erl + erlc $(EFLAGS) -odependencies/z-1.0/ebin dependencies/z-1.0/src/z1.erl diff --git a/lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/ebin/x.app b/lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/ebin/x.app new file mode 100644 index 0000000000..ccaab8a8c7 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/ebin/x.app @@ -0,0 +1,7 @@ +% -*-erlang-*- +{application, x, + [{description, "Main application in reltool dependency test"}, + {vsn, "1.0"}, + {modules, [x1,x2,x3]}, + {registered, []}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/src/x1.erl b/lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/src/x1.erl new file mode 100644 index 0000000000..bf1e7f9279 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/src/x1.erl @@ -0,0 +1,5 @@ +-module(x1). +-compile(export_all). + +f() -> + ok. diff --git a/lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/src/x2.erl b/lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/src/x2.erl new file mode 100644 index 0000000000..82191ba278 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/src/x2.erl @@ -0,0 +1,5 @@ +-module(x2). +-compile(export_all). + +f() -> + y1:f(). diff --git a/lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/src/x3.erl b/lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/src/x3.erl new file mode 100644 index 0000000000..618c75c9a7 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/src/x3.erl @@ -0,0 +1,5 @@ +-module(x3). +-compile(export_all). + +f() -> + y2:f(). diff --git a/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/ebin/y.app b/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/ebin/y.app new file mode 100644 index 0000000000..d9dac371d7 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/ebin/y.app @@ -0,0 +1,7 @@ +% -*-erlang-*- +{application, y, + [{description, "Library application in reltool dependency test"}, + {vsn, "1.0"}, + {modules, [y1,y2]}, + {registered, []}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/src/y1.erl b/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/src/y1.erl new file mode 100644 index 0000000000..dd21b33292 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/src/y1.erl @@ -0,0 +1,5 @@ +-module(y1). +-compile(export_all). + +f() -> + z1:f(). diff --git a/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/src/y2.erl b/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/src/y2.erl new file mode 100644 index 0000000000..bf8ddf6080 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/src/y2.erl @@ -0,0 +1,5 @@ +-module(y2). +-compile(export_all). + +f() -> + ok. diff --git a/lib/reltool/test/reltool_server_SUITE_data/dependencies/z-1.0/ebin/z.app b/lib/reltool/test/reltool_server_SUITE_data/dependencies/z-1.0/ebin/z.app new file mode 100644 index 0000000000..437a0968e9 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/dependencies/z-1.0/ebin/z.app @@ -0,0 +1,7 @@ +% -*-erlang-*- +{application, z, + [{description, "Library application in reltool dependency test"}, + {vsn, "1.0"}, + {modules, [z1]}, + {registered, []}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/reltool/test/reltool_server_SUITE_data/dependencies/z-1.0/src/z1.erl b/lib/reltool/test/reltool_server_SUITE_data/dependencies/z-1.0/src/z1.erl new file mode 100644 index 0000000000..97ef90b87f --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/dependencies/z-1.0/src/z1.erl @@ -0,0 +1,5 @@ +-module(z1). +-compile(export_all). + +f() -> + ok. -- cgit v1.2.3 From 06ea8c9f62e265bc045eb92d522e84a5f208f8cd Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 13 Jan 2012 14:54:50 +0100 Subject: [reltool] Ensure that tests do not try to start node if target was not created OTP-9794 Some test cases in reltool_server_SUITE tried to create a target system and then start a node running this system. If creation of the target system failed, an attempt would still be made to start the node. This has been corrected so the test is immediately terminated if the target system could not be created. --- lib/reltool/test/reltool_server_SUITE.erl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl index 90288020f1..086ada41e0 100644 --- a/lib/reltool/test/reltool_server_SUITE.erl +++ b/lib/reltool/test/reltool_server_SUITE.erl @@ -324,7 +324,7 @@ create_target(_Config) -> ?m(ok, reltool_utils:recursive_delete(TargetDir)), ?m(ok, file:make_dir(TargetDir)), ?log("SPEC: ~p\n", [reltool:get_target_spec([{config, Config}])]), - ?m(ok, reltool:create_target([{config, Config}], TargetDir)), + ok = ?m(ok, reltool:create_target([{config, Config}], TargetDir)), Erl = filename:join([TargetDir, "bin", "erl"]), {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), @@ -357,7 +357,7 @@ create_embedded(_Config) -> TargetDir = filename:join([?WORK_DIR, "target_embedded"]), ?m(ok, reltool_utils:recursive_delete(TargetDir)), ?m(ok, file:make_dir(TargetDir)), - ?m(ok, reltool:create_target([{config, Config}], TargetDir)), + ok = ?m(ok, reltool:create_target([{config, Config}], TargetDir)), Erl = filename:join([TargetDir, "bin", "erl"]), {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), @@ -387,7 +387,7 @@ create_standalone(_Config) -> TargetDir = filename:join([?WORK_DIR, "target_standalone"]), ?m(ok, reltool_utils:recursive_delete(TargetDir)), ?m(ok, file:make_dir(TargetDir)), - ?m(ok, reltool:create_target([{config, Config}], TargetDir)), + ok = ?m(ok, reltool:create_target([{config, Config}], TargetDir)), BinDir = filename:join([TargetDir, "bin"]), Erl = filename:join([BinDir, "erl"]), @@ -431,10 +431,10 @@ create_old_target(_Config) -> TargetDir = filename:join([?WORK_DIR, "target_old_style"]), ?m(ok, reltool_utils:recursive_delete(TargetDir)), ?m(ok, file:make_dir(TargetDir)), - ?m(ok, reltool:create_target([{config, Config}], TargetDir)), + ok = ?m(ok, reltool:create_target([{config, Config}], TargetDir)), %% io:format("Will fail on Windows (should patch erl.ini)\n", []), - ?m(ok, reltool:install(RelName2, TargetDir)), + ok = ?m(ok, reltool:install(RelName2, TargetDir)), Erl = filename:join([TargetDir, "bin", "erl"]), {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), @@ -471,7 +471,7 @@ otp_9229_exclude_app(Config) -> ?m(ok, file:make_dir(TargetDir)), ?log("SPEC: ~p\n", [reltool:get_target_spec([{config, ExclApp}])]), ?m({ok,["Module mylib exists in applications x and y. Using module from application x."]}, reltool:get_status([{config, ExclApp}])), - ?m(ok, reltool:create_target([{config, ExclApp}], TargetDir)), + ok = ?m(ok, reltool:create_target([{config, ExclApp}], TargetDir)), Erl = filename:join([TargetDir, "bin", "erl"]), {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), @@ -518,7 +518,7 @@ otp_9229_exclude_mod(Config) -> ?m(ok, file:make_dir(TargetDir)), ?log("SPEC: ~p\n", [reltool:get_target_spec([{config, ExclMod}])]), ?m({ok,["Module mylib exists in applications x and y. Using module from application x."]}, reltool:get_status([{config, ExclMod}])), - ?m(ok, reltool:create_target([{config, ExclMod}], TargetDir)), + ok = ?m(ok, reltool:create_target([{config, ExclMod}], TargetDir)), Erl = filename:join([TargetDir, "bin", "erl"]), {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), -- cgit v1.2.3 From 610280492bca33c851eabda0a2daf5cf141ea090 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 20 Jan 2012 17:10:31 +0100 Subject: [reltool] Add test case for reltool_server:get_mod OTP-9794 This test case revealed a bug that occurs when calling reltool_server:get_mod after reltool_server:undo_config. get_mod reads from the module table (ets) and not from the reltool_server state, while undo_config only changes the state. This bug has been corrected, so undo_config now updates both state and tables (it does the same as set_sys). --- lib/reltool/src/reltool_server.erl | 30 ++++++-- lib/reltool/src/reltool_sys_win.erl | 25 +++++-- lib/reltool/test/reltool_server_SUITE.erl | 116 ++++++++++++++++++++++-------- 3 files changed, 133 insertions(+), 38 deletions(-) (limited to 'lib') diff --git a/lib/reltool/src/reltool_server.erl b/lib/reltool/src/reltool_server.erl index fdb9e08a90..7dc2d21158 100644 --- a/lib/reltool/src/reltool_server.erl +++ b/lib/reltool/src/reltool_server.erl @@ -266,12 +266,30 @@ loop(#state{common = C, sys = Sys} = S) -> reltool_utils:reply(ReplyTo, Ref, Status3), ?MODULE:loop(S6); {call, ReplyTo, Ref, undo_config} -> - S2 = S#state{sys = S#state.old_sys, - old_sys = S#state.sys, - status = S#state.old_status, - old_status = S#state.status}, - reltool_utils:reply(ReplyTo, Ref, ok), - ?MODULE:loop(S2); + OldSys = S#state.old_sys, + S2 = S#state{sys = OldSys, + old_sys = Sys}, + %%! Possibly skip old_status here, since we do all job again! + %%! If so, consider if it is correct to use Force or not - + %%! since warnings from refresh_app will not re-appear here + %%! in undo if Force==false. + Force = + (OldSys#sys.root_dir =/= Sys#sys.root_dir) orelse + (OldSys#sys.lib_dirs =/= Sys#sys.lib_dirs) orelse + (OldSys#sys.escripts =/= Sys#sys.escripts), + + {S3, Status} = refresh(S2, Force, S#state.old_status), + {S4, Status2} = analyse(S3, Status), + S5 = + case Status2 of + {ok, _Warnings} -> % BUGBUG: handle warnings + S4#state{status = Status2, old_status = S#state.status}; + {error, _} -> + %% Keep old state + S + end, + reltool_utils:reply(ReplyTo, Ref, Status2), + ?MODULE:loop(S5); {call, ReplyTo, Ref, {get_rel, RelName}} -> Sys = S#state.sys, Reply = diff --git a/lib/reltool/src/reltool_sys_win.erl b/lib/reltool/src/reltool_sys_win.erl index 8b0f64eb45..f29a95af38 100644 --- a/lib/reltool/src/reltool_sys_win.erl +++ b/lib/reltool/src/reltool_sys_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 @@ -1290,7 +1290,15 @@ reset_config(#state{status_bar = Bar} = S) -> undo_config(#state{status_bar = Bar} = S) -> wxStatusBar:setStatusText(Bar, "Processing libraries..."), - ok = reltool_server:undo_config(S#state.server_pid), + case reltool_server:undo_config(S#state.server_pid) of + {ok, []} -> + ok; + {ok, Warnings} -> + Msg = lists:flatten([[W, $\n] || W <- Warnings]), + display_message(Msg, ?wxICON_WARNING); + {error, Reason} -> + display_message(Reason, ?wxICON_ERROR) + end, refresh(S). load_config(#state{status_bar = Bar, config_file = OldFile} = S) -> @@ -1444,8 +1452,17 @@ undo_dialog(S, Warnings) -> ?wxID_OK -> true; ?wxID_CANCEL -> - reltool_server:undo_config(S#state.server_pid), - false + case reltool_server:undo_config(S#state.server_pid) of + {ok, _} -> + false; + {error, Reason} -> + Msg = "FATAL - undo failed:\n\n" ++ + Reason ++ + "\n\nTerminating...", + display_message(Msg, ?wxICON_ERROR), + io:format("~p(~p): ~s\n", [?MODULE, ?LINE, Reason]), + exit(Reason) + end end. display_message(Message, Icon) -> diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl index 086ada41e0..79c6854465 100644 --- a/lib/reltool/test/reltool_server_SUITE.erl +++ b/lib/reltool/test/reltool_server_SUITE.erl @@ -56,7 +56,7 @@ all() -> create_script, create_target, create_embedded, create_standalone, create_old_target, otp_9135, otp_9229_exclude_app, otp_9229_exclude_mod, - get_apps, set_app_and_undo, set_apps_and_undo, + get_apps, get_mod, set_app_and_undo, set_apps_and_undo, load_config_and_undo, reset_config_and_undo, save_config, dependencies]. @@ -595,6 +595,29 @@ get_apps(_Config) -> ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +get_mod(_Config) -> + Sys = {sys,[{app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,tools,[{incl_cond,derived}]}, + {app,runtime_tools,[{incl_cond,exclude}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + + %% Read app and get a module from the #app record + {ok,Tools} = ?msym({ok,#app{name=tools}}, reltool_server:get_app(Pid,tools)), + Cover = lists:keyfind(cover,#mod.name,Tools#app.mods), + + %% get_mod - and check that it is equal to the one in #app.mods + ?m({ok,Cover}, reltool_server:get_mod(Pid,cover)), + + ?m(ok, reltool:stop(Pid)), + + ok. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% set_app_and_undo(Config) -> Sys = {sys,[{lib_dirs,[filename:join(datadir(Config),"faulty_app_file")]}, {incl_cond, exclude}, @@ -606,25 +629,32 @@ set_app_and_undo(Config) -> {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), ?m({ok, Sys}, reltool:get_config(Pid)), - %% Exclude one module with set_app + %% Get app and mod {ok,Tools} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), - Mods = Tools#app.mods, - Cover = lists:keyfind(cover,#mod.name,Mods), + {ok,Cover} = ?msym({ok,#mod{name=cover, is_included=true}}, + reltool_server:get_mod(Pid,cover)), + + %% Exclude one module with set_app ExclCover = Cover#mod{incl_cond=exclude}, + Mods = Tools#app.mods, Tools1 = Tools#app{mods = lists:keyreplace(cover,#mod.name,Mods,ExclCover)}, {ok,ToolsNoCover,[]} = ?msym({ok,_,[]}, reltool_server:set_app(Pid,Tools1)), + + %% Check that the module is no longer included ?m({ok,ToolsNoCover}, reltool_server:get_app(Pid,tools)), + {ok,NoIncludeCover} = ?msym({ok,#mod{name=cover, is_included=false}}, + reltool_server:get_mod(Pid,cover)), %% Undo - ?m(ok, reltool_server:undo_config(Pid)), - ?m({ok,Tools}, reltool_server:get_app(Pid,tools)), %%! warning can come twice here... :( - ?msym({ok,["a: Cannot parse app file"++_|_]}, reltool:get_status(Pid)), + ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:undo_config(Pid)), + ?m({ok,Tools}, reltool_server:get_app(Pid,tools)), + ?m({ok,Cover}, reltool_server:get_mod(Pid,cover)), %% Undo again, to check that it toggles - ?m(ok, reltool_server:undo_config(Pid)), + ?m({ok,[]}, reltool_server:undo_config(Pid)), ?m({ok,ToolsNoCover}, reltool_server:get_app(Pid,tools)), - ?m({ok,[]}, reltool:get_status(Pid)), + ?m({ok,NoIncludeCover}, reltool_server:get_mod(Pid,cover)), ?m(ok, reltool:stop(Pid)), ok. @@ -641,24 +671,34 @@ set_apps_and_undo(Config) -> {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), ?m({ok, Sys}, reltool:get_config(Pid)), - %% Exclude one application with set_apps + %% Get app and mod {ok,Tools} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), + ?m(true, Tools#app.is_pre_included), + ?m(true, Tools#app.is_included), + {ok,Cover} = ?msym({ok,#mod{name=cover, is_included=true}}, + reltool_server:get_mod(Pid,cover)), + + %% Exclude one application with set_apps ExclTools = Tools#app{incl_cond=exclude}, ?m({ok,[]}, reltool_server:set_apps(Pid,[ExclTools])), + + %% Check that the app and its modules (one of them) are no longer included {ok,NoTools} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), ?m(false, NoTools#app.is_pre_included), ?m(false, NoTools#app.is_included), + {ok,NoIncludeCover} = ?msym({ok,#mod{name=cover, is_included=false}}, + reltool_server:get_mod(Pid,cover)), %% Undo - ?m(ok, reltool_server:undo_config(Pid)), - ?m({ok,Tools}, reltool_server:get_app(Pid,tools)), %%! warning can come twice here... :( - ?msym({ok,["a: Cannot parse app file"++_|_]}, reltool:get_status(Pid)), + ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:undo_config(Pid)), + ?m({ok,Tools}, reltool_server:get_app(Pid,tools)), + ?m({ok,Cover}, reltool_server:get_mod(Pid,cover)), %% Undo again, to check that it toggles - ?m(ok, reltool_server:undo_config(Pid)), + ?m({ok,[]}, reltool_server:undo_config(Pid)), ?m({ok,NoTools}, reltool_server:get_app(Pid,tools)), - ?m({ok,[]}, reltool:get_status(Pid)), + ?m({ok,NoIncludeCover}, reltool_server:get_mod(Pid,cover)), ?m(ok, reltool:stop(Pid)), ok. @@ -674,12 +714,16 @@ load_config_and_undo(Config) -> {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys1}])), ?m({ok, Sys1}, reltool:get_config(Pid)), - %% Check that tools is included + %% Get app and mod {ok,Tools1} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), ?m(true, Tools1#app.is_pre_included), ?m(true, Tools1#app.is_included), + {ok,Cover1} = ?msym({ok,#mod{name=cover, + is_included=true, + is_pre_included=true}}, + reltool_server:get_mod(Pid,cover)), - %% Exclude one application with set_apps + %% Change tools from include to derived by loading new config Sys2 = {sys,[{lib_dirs,[filename:join(datadir(Config),"faulty_app_file")]}, {incl_cond, exclude}, {app,kernel,[{incl_cond,include}]}, @@ -695,16 +739,20 @@ load_config_and_undo(Config) -> {ok,Tools2} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), ?m(undefined, Tools2#app.is_pre_included), ?m(true, Tools2#app.is_included), + {ok,Cover2} = ?msym({ok,#mod{name=cover, + is_included=true, + is_pre_included=undefined}}, + reltool_server:get_mod(Pid,cover)), %% Undo - ?m(ok, reltool_server:undo_config(Pid)), + ?m({ok,[]}, reltool_server:undo_config(Pid)), ?m({ok,Tools1}, reltool_server:get_app(Pid,tools)), - ?m({ok,[]}, reltool:get_status(Pid)), + ?m({ok,Cover1}, reltool_server:get_mod(Pid,cover)), %% Undo again, to check that it toggles - ?m(ok, reltool_server:undo_config(Pid)), + ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:undo_config(Pid)), ?m({ok,Tools2}, reltool_server:get_app(Pid,tools)), - ?msym({ok,["a: Cannot parse app file"++_]}, reltool:get_status(Pid)), + ?m({ok,Cover2}, reltool_server:get_mod(Pid,cover)), ?m(ok, reltool:stop(Pid)), ok. @@ -722,12 +770,16 @@ reset_config_and_undo(Config) -> {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys1}])), ?m({ok, Sys1}, reltool:get_config(Pid)), - %% Check that tools is included + %% Get app and mod {ok,Tools1} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), ?m(true, Tools1#app.is_pre_included), ?m(true, Tools1#app.is_included), + {ok,Cover1} = ?msym({ok,#mod{name=cover, + is_included=true, + is_pre_included=true}}, + reltool_server:get_mod(Pid,cover)), - %% Exclude one application with set_apps + %% Exclude tools by loading new config Sys2 = {sys,[{incl_cond, exclude}, {app,kernel,[{incl_cond,include}]}, {app,sasl,[{incl_cond,include}]}, @@ -739,17 +791,27 @@ reset_config_and_undo(Config) -> {ok,Tools2} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), ?m(false, Tools2#app.is_pre_included), ?m(false, Tools2#app.is_included), + {ok,Cover2} = ?msym({ok,#mod{name=cover, + is_included=false, + is_pre_included=false}}, + reltool_server:get_mod(Pid,cover)), %% Reset %%! warning can come twice here... :( ?msym({ok,["a: Cannot parse app file"++_|_]}, reltool_server:reset_config(Pid)), ?m({ok,Tools1}, reltool_server:get_app(Pid,tools)), + ?m({ok,Cover1}, reltool_server:get_mod(Pid,cover)), - %% Undo again, to check that it toggles - ?m(ok, reltool_server:undo_config(Pid)), + %% Undo + ?m({ok,[]}, reltool_server:undo_config(Pid)), ?m({ok,Tools2}, reltool_server:get_app(Pid,tools)), - ?m({ok,[]}, reltool:get_status(Pid)), + ?m({ok,Cover2}, reltool_server:get_mod(Pid,cover)), + + %% Undo again, to check that it toggles + ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:undo_config(Pid)), + ?m({ok,Tools1}, reltool_server:get_app(Pid,tools)), + ?m({ok,Cover1}, reltool_server:get_mod(Pid,cover)), ?m(ok, reltool:stop(Pid)), ok. @@ -851,8 +913,6 @@ save_config(Config) -> %% Test includes x and derives y and z. %% dependencies(Config) -> - PrivDir = ?config(priv_dir,Config), - %% Default: all modules included => y and z are included (derived) Sys = {sys,[{lib_dirs,[filename:join(datadir(Config),"dependencies")]}, {incl_cond, exclude}, -- cgit v1.2.3 From f515a08b3a57c854ee547e6c84c39e2fd14a27da Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Tue, 24 Jan 2012 15:46:31 +0100 Subject: [reltool] Add more tests OTP-9794 Test cases are added for the following functions: * reltool_server:get_sys * reltool_server:set_sys * reltool_server:get_status * reltool_server:gen_rel_files * reltool:eval_target_spec * reltool:get_server --- lib/reltool/test/reltool_server_SUITE.erl | 156 +++++++++++++++++++++++++++--- lib/reltool/test/reltool_wx_SUITE.erl | 8 +- 2 files changed, 151 insertions(+), 13 deletions(-) (limited to 'lib') diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl index 79c6854465..fc2d844d07 100644 --- a/lib/reltool/test/reltool_server_SUITE.erl +++ b/lib/reltool/test/reltool_server_SUITE.erl @@ -54,11 +54,11 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [start_server, set_config, get_config, create_release, create_script, create_target, create_embedded, - create_standalone, create_old_target, + create_standalone, create_old_target, eval_target_spec, otp_9135, otp_9229_exclude_app, otp_9229_exclude_mod, - get_apps, get_mod, set_app_and_undo, set_apps_and_undo, - load_config_and_undo, reset_config_and_undo, save_config, - dependencies]. + get_apps, get_mod, get_sys, set_app_and_undo, set_apps_and_undo, + set_sys_and_undo, load_config_and_undo, reset_config_and_undo, + gen_rel_files, save_config, dependencies]. groups() -> []. @@ -442,6 +442,38 @@ create_old_target(_Config) -> ok. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Generate target system with eval_target_spec/3 + +eval_target_spec(_Config) -> + %% Configure the server + RelName1 = "Just testing", + RelName2 = "Just testing with SASL", + RelVsn = "1.0", + Config = + {sys, + [ + {root_dir, code:root_dir()}, + {lib_dirs, []}, + {boot_rel, RelName2}, + {rel, RelName1, RelVsn, [stdlib, kernel]}, + {rel, RelName2, RelVsn, [sasl, stdlib, kernel]}, + {app, sasl, [{incl_cond, include}]} + ]}, + + %% Generate target file + TargetDir = filename:join([?WORK_DIR, "eval_target_spec"]), + ?m(ok, reltool_utils:recursive_delete(TargetDir)), + ?m(ok, file:make_dir(TargetDir)), + {ok, Spec} = ?msym({ok,_}, reltool:get_target_spec([{config, Config}])), + ok = ?m(ok, reltool:eval_target_spec(Spec, code:root_dir(), TargetDir)), + + Erl = filename:join([TargetDir, "bin", "erl"]), + {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), + ?msym(ok, stop_node(Node)), + + ok. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% OTP-9229 - handle duplicated module names, i.e. same module name %% exists in two applications. @@ -556,9 +588,8 @@ otp_9229_exclude_mod(Config) -> %% load_config %% reset_config %% -%% Also, for each operation which manipulates the config test -%% undo_config - that it is properly undone, and that warnings are -%% re-displayed. +%% Also, for each operation which manipulates the config, test +%% get_status and undo_config. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% get_apps(_Config) -> Sys = {sys,[{app,kernel,[{incl_cond,include}]}, @@ -592,7 +623,6 @@ get_apps(_Config) -> ?m(Number,length(AllN)), ?m(ok, reltool:stop(Pid)), - ok. @@ -613,10 +643,24 @@ get_mod(_Config) -> ?m({ok,Cover}, reltool_server:get_mod(Pid,cover)), ?m(ok, reltool:stop(Pid)), - ok. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +get_sys(_Config) -> + Sys = {sys,[{app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,tools,[{incl_cond,derived}]}, + {app,runtime_tools,[{incl_cond,exclude}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + + RootDir = code:root_dir(), + ?msym({ok,#sys{root_dir=RootDir,apps=undefined}},reltool_server:get_sys(Pid)), + + ?m(ok, reltool:stop(Pid)), + ok. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% set_app_and_undo(Config) -> Sys = {sys,[{lib_dirs,[filename:join(datadir(Config),"faulty_app_file")]}, @@ -628,6 +672,7 @@ set_app_and_undo(Config) -> {app,tools,[{incl_cond,include}]}]}, {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), ?m({ok, Sys}, reltool:get_config(Pid)), + ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:get_status(Pid)), %% Get app and mod {ok,Tools} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), @@ -639,6 +684,7 @@ set_app_and_undo(Config) -> Mods = Tools#app.mods, Tools1 = Tools#app{mods = lists:keyreplace(cover,#mod.name,Mods,ExclCover)}, {ok,ToolsNoCover,[]} = ?msym({ok,_,[]}, reltool_server:set_app(Pid,Tools1)), + ?m({ok,[]}, reltool_server:get_status(Pid)), %% Check that the module is no longer included ?m({ok,ToolsNoCover}, reltool_server:get_app(Pid,tools)), @@ -650,11 +696,13 @@ set_app_and_undo(Config) -> ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:undo_config(Pid)), ?m({ok,Tools}, reltool_server:get_app(Pid,tools)), ?m({ok,Cover}, reltool_server:get_mod(Pid,cover)), + ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:get_status(Pid)), %% Undo again, to check that it toggles ?m({ok,[]}, reltool_server:undo_config(Pid)), ?m({ok,ToolsNoCover}, reltool_server:get_app(Pid,tools)), ?m({ok,NoIncludeCover}, reltool_server:get_mod(Pid,cover)), + ?m({ok,[]}, reltool_server:get_status(Pid)), ?m(ok, reltool:stop(Pid)), ok. @@ -670,6 +718,7 @@ set_apps_and_undo(Config) -> {app,tools,[{incl_cond,include}]}]}, {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), ?m({ok, Sys}, reltool:get_config(Pid)), + ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:get_status(Pid)), %% Get app and mod {ok,Tools} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), @@ -681,6 +730,7 @@ set_apps_and_undo(Config) -> %% Exclude one application with set_apps ExclTools = Tools#app{incl_cond=exclude}, ?m({ok,[]}, reltool_server:set_apps(Pid,[ExclTools])), + ?m({ok,[]}, reltool_server:get_status(Pid)), %% Check that the app and its modules (one of them) are no longer included {ok,NoTools} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), @@ -694,16 +744,53 @@ set_apps_and_undo(Config) -> ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:undo_config(Pid)), ?m({ok,Tools}, reltool_server:get_app(Pid,tools)), ?m({ok,Cover}, reltool_server:get_mod(Pid,cover)), + ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:get_status(Pid)), %% Undo again, to check that it toggles ?m({ok,[]}, reltool_server:undo_config(Pid)), ?m({ok,NoTools}, reltool_server:get_app(Pid,tools)), ?m({ok,NoIncludeCover}, reltool_server:get_mod(Pid,cover)), + ?m({ok,[]}, reltool_server:get_status(Pid)), ?m(ok, reltool:stop(Pid)), ok. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +set_sys_and_undo(Config) -> + Sys1 = {sys,[{incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,tools,[{incl_cond,include}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys1}])), + ?m({ok,[]}, reltool_server:get_status(Pid)), + + %% Read sys record + {ok, SysRec} = reltool_server:get_sys(Pid), + + %% Set lib dirs by call to set_sys + NewLib = filename:join(datadir(Config),"faulty_app_file"), + NewLibDirs = [NewLib | SysRec#sys.lib_dirs], + NewSysRec = SysRec#sys{lib_dirs=NewLibDirs}, + ?msym({ok,["a: Cannot parse app file"++_|_]}, + reltool_server:set_sys(Pid, NewSysRec)), + ?m({ok,NewSysRec}, reltool_server:get_sys(Pid)), + ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:get_status(Pid)), + + %% Undo + ?m({ok,[]}, reltool_server:undo_config(Pid)), + ?m({ok,SysRec}, reltool_server:get_sys(Pid)), + ?m({ok,[]}, reltool_server:get_status(Pid)), + + %% Undo again, to check that it toggles + ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:undo_config(Pid)), + ?m({ok,NewSysRec}, reltool_server:get_sys(Pid)), + ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:get_status(Pid)), + + ?m(ok, reltool:stop(Pid)), + ok. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% load_config_and_undo(Config) -> Sys1 = {sys,[{incl_cond, exclude}, @@ -713,6 +800,7 @@ load_config_and_undo(Config) -> {app,tools,[{incl_cond,include}]}]}, {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys1}])), ?m({ok, Sys1}, reltool:get_config(Pid)), + ?m({ok,[]}, reltool_server:get_status(Pid)), %% Get app and mod {ok,Tools1} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), @@ -725,14 +813,17 @@ load_config_and_undo(Config) -> %% Change tools from include to derived by loading new config Sys2 = {sys,[{lib_dirs,[filename:join(datadir(Config),"faulty_app_file")]}, - {incl_cond, exclude}, + {app,a,[{incl_cond,include}]}, {app,kernel,[{incl_cond,include}]}, {app,sasl,[{incl_cond,include}]}, {app,stdlib,[{incl_cond,include}]}, - {app,tools,[{incl_cond,derived}]}, - {app,a,[{incl_cond,include}]}]}, + {app,tools,[{incl_cond,derived}]}]}, ?msym({ok,["a: Cannot parse app file"++_]}, reltool_server:load_config(Pid,Sys2)), +%%% OTP-0702, 15) ?m({ok, Sys2}, reltool:get_config(Pid)), +%%% Note that {incl_cond,exclude} is removed compared to Sys1 - +%%% config is merged, not overwritten - is this correct??? + ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:get_status(Pid)), %% Check that tools is included (since it is used by sasl) but not %% pre-included (neither included or excluded => undefined) @@ -748,11 +839,13 @@ load_config_and_undo(Config) -> ?m({ok,[]}, reltool_server:undo_config(Pid)), ?m({ok,Tools1}, reltool_server:get_app(Pid,tools)), ?m({ok,Cover1}, reltool_server:get_mod(Pid,cover)), + ?m({ok,[]}, reltool_server:get_status(Pid)), %% Undo again, to check that it toggles ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:undo_config(Pid)), ?m({ok,Tools2}, reltool_server:get_app(Pid,tools)), ?m({ok,Cover2}, reltool_server:get_mod(Pid,cover)), + ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:get_status(Pid)), ?m(ok, reltool:stop(Pid)), ok. @@ -769,6 +862,7 @@ reset_config_and_undo(Config) -> {app,tools,[{incl_cond,include}]}]}, {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys1}])), ?m({ok, Sys1}, reltool:get_config(Pid)), + ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:get_status(Pid)), %% Get app and mod {ok,Tools1} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), @@ -786,6 +880,7 @@ reset_config_and_undo(Config) -> {app,stdlib,[{incl_cond,include}]}, {app,tools,[{incl_cond,exclude}]}]}, ?m({ok,[]}, reltool_server:load_config(Pid,Sys2)), + ?m({ok,[]}, reltool_server:get_status(Pid)), %% Check that tools is excluded {ok,Tools2} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), @@ -802,16 +897,53 @@ reset_config_and_undo(Config) -> reltool_server:reset_config(Pid)), ?m({ok,Tools1}, reltool_server:get_app(Pid,tools)), ?m({ok,Cover1}, reltool_server:get_mod(Pid,cover)), + ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:get_status(Pid)), %% Undo ?m({ok,[]}, reltool_server:undo_config(Pid)), ?m({ok,Tools2}, reltool_server:get_app(Pid,tools)), ?m({ok,Cover2}, reltool_server:get_mod(Pid,cover)), + ?m({ok,[]}, reltool_server:get_status(Pid)), %% Undo again, to check that it toggles ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:undo_config(Pid)), ?m({ok,Tools1}, reltool_server:get_app(Pid,tools)), ?m({ok,Cover1}, reltool_server:get_mod(Pid,cover)), + ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:get_status(Pid)), + + ?m(ok, reltool:stop(Pid)), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +gen_rel_files(_Config) -> + %% Configure the server + RelName = "gen_fel_files_test", + RelVsn = "1.0", + Sys = + {sys, + [ + {lib_dirs, []}, + {boot_rel, RelName}, + {rel, RelName, RelVsn, [kernel, stdlib]} + ]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + + %% Generate .rel, .script and .boot + Dir = filename:join(?WORK_DIR,"gen_rel_files"), + ok = file:make_dir(Dir), + ?m({ok,[]}, reltool_server:gen_rel_files(Pid,Dir)), + + Script = RelName ++ ".script", + Rel = RelName ++ ".rel", + Boot = RelName ++ ".boot", + {ok,Files} = ?msym({ok,_}, file:list_dir(Dir)), + [Boot,Rel,Script] = lists:sort(Files), + + %% Check that contents is reasonable + {ok,[S]} = ?msym({ok,[{script,_,_}]},file:consult(filename:join(Dir,Script))), + ?msym({ok,[{release,_,_,_}]}, file:consult(filename:join(Dir,Rel))), + {ok,Bin} = ?msym({ok,_}, file:read_file(filename:join(Dir,Boot))), + ?m(S,binary_to_term(Bin)), ?m(ok, reltool:stop(Pid)), ok. diff --git a/lib/reltool/test/reltool_wx_SUITE.erl b/lib/reltool/test/reltool_wx_SUITE.erl index 424bc7d189..51f433f206 100644 --- a/lib/reltool/test/reltool_wx_SUITE.erl +++ b/lib/reltool/test/reltool_wx_SUITE.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 @@ -63,6 +63,12 @@ start_all_windows(_Config) -> {ok, SysPid} = ?msym({ok, _}, reltool:start([{trap_exit, false}])), {ok, AppPid} = ?msym({ok, _}, reltool_sys_win:open_app(SysPid, stdlib)), ?msym({ok, _}, reltool_app_win:open_mod(AppPid, escript)), + + %% Test that server pid can be fetched, and that server is alive + {ok, Server} = ?msym({ok,_}, reltool:get_server(SysPid)), + ?m(true, erlang:is_process_alive(Server)), + ?m({ok,{sys,[]}}, reltool:get_config(Server)), + timer:sleep(timer:seconds(10)), ?m(ok, reltool:stop(SysPid)), -- cgit v1.2.3 From 3fef8ee7031a16a038c9f87dc8cf8ea44064e6ed Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 25 Jan 2012 16:24:35 +0100 Subject: [reltool] Add more GUI tests OTP-9794 This commit adds a test suite for manual testing of the reltool GUI. By use of test_server:break/1 function, it guides the user through a set of tests in the GUI. Some minor checks are also added to the automatic GUI test, reltool_wx_SUITE. --- lib/reltool/test/Makefile | 3 +- lib/reltool/test/reltool.spec | 1 + lib/reltool/test/reltool_manual_gui_SUITE.erl | 247 +++++++++++++++++++++ .../reltool_manual_gui_SUITE_data/Makefile.src | 28 +++ .../dependencies/x-1.0/ebin/x.app | 7 + .../dependencies/x-1.0/src/x1.erl | 5 + .../dependencies/x-1.0/src/x2.erl | 5 + .../dependencies/x-1.0/src/x3.erl | 5 + .../dependencies/y-1.0/ebin/y.app | 7 + .../dependencies/y-1.0/src/y1.erl | 5 + .../dependencies/y-1.0/src/y2.erl | 5 + .../dependencies/y-1.0/src/y3.erl | 5 + .../dependencies/z-1.0/ebin/z.app | 7 + .../dependencies/z-1.0/src/z1.erl | 5 + .../faulty_app_file/a-1.0/ebin/a.app | 1 + .../faulty_app_file/a-1.0/src/a.erl | 49 ++++ .../faulty_app_file/a-1.0/src/a_sup.erl | 37 +++ lib/reltool/test/reltool_wx_SUITE.erl | 37 ++- 18 files changed, 455 insertions(+), 4 deletions(-) create mode 100644 lib/reltool/test/reltool_manual_gui_SUITE.erl create mode 100644 lib/reltool/test/reltool_manual_gui_SUITE_data/Makefile.src create mode 100644 lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/ebin/x.app create mode 100644 lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/src/x1.erl create mode 100644 lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/src/x2.erl create mode 100644 lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/src/x3.erl create mode 100644 lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/ebin/y.app create mode 100644 lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/src/y1.erl create mode 100644 lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/src/y2.erl create mode 100644 lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/src/y3.erl create mode 100644 lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/z-1.0/ebin/z.app create mode 100644 lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/z-1.0/src/z1.erl create mode 100644 lib/reltool/test/reltool_manual_gui_SUITE_data/faulty_app_file/a-1.0/ebin/a.app create mode 100644 lib/reltool/test/reltool_manual_gui_SUITE_data/faulty_app_file/a-1.0/src/a.erl create mode 100644 lib/reltool/test/reltool_manual_gui_SUITE_data/faulty_app_file/a-1.0/src/a_sup.erl (limited to 'lib') diff --git a/lib/reltool/test/Makefile b/lib/reltool/test/Makefile index 767454b66a..d8a7adb837 100644 --- a/lib/reltool/test/Makefile +++ b/lib/reltool/test/Makefile @@ -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 @@ -28,6 +28,7 @@ MODULES= \ reltool_app_SUITE \ reltool_wx_SUITE \ reltool_server_SUITE \ + reltool_manual_gui_SUITE \ reltool_test_lib diff --git a/lib/reltool/test/reltool.spec b/lib/reltool/test/reltool.spec index 2995720105..2501a7a203 100644 --- a/lib/reltool/test/reltool.spec +++ b/lib/reltool/test/reltool.spec @@ -1 +1,2 @@ {suites,"../reltool_test",all}. +{skip_suites,"../reltool_test",[reltool_manual_gui_SUITE],"Manual only"}. diff --git a/lib/reltool/test/reltool_manual_gui_SUITE.erl b/lib/reltool/test/reltool_manual_gui_SUITE.erl new file mode 100644 index 0000000000..c6b1d56988 --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE.erl @@ -0,0 +1,247 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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 +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% + +-module(reltool_manual_gui_SUITE). + +-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2, + init_per_suite/1, end_per_suite/1, + init_per_testcase/2, end_per_testcase/2]). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include("reltool_test_lib.hrl"). + +%% Initialization functions. +init_per_suite(Config) -> + reltool_test_lib:wx_init_per_suite(Config). + +end_per_suite(Config) -> + reltool_test_lib:wx_end_per_suite(Config). + +init_per_testcase(Func,Config) -> + reltool_test_lib:init_per_testcase(Func,Config). +end_per_testcase(Func,Config) -> + reltool_test_lib:end_per_testcase(Func,Config). + +%% SUITE specification +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [config, depgraphs]. + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + +%% The test cases + +%% Semi-automatic walkthrough of the GUI +config(Config) -> + DataDir = ?config(data_dir,Config), + PrivDir = ?config(priv_dir,Config), + {ok, SysPid} = ?msym({ok, _}, reltool:start([])), + link(SysPid), + + SimpleConfigFile = create_simple_config(PrivDir), + WarningConfigFile = create_warning_config(PrivDir,DataDir), + + break("there are no modules in the 'Included' and 'Excluded' columns", + {"load configuration ~p",[SimpleConfigFile]}), + break("kernel, stdlib and sasl are included and all other are excluded", + "undo"), + break("we are back to default - no included and no excluded applications", + "undo again"), + break("kernel, stdlib and sasl are included and all other are excluded", + {"load configuration ~p, but click 'cancel' in the warning dialog", + [WarningConfigFile]}), + break("no change is done", + "load same configuration again and this time click 'ok' in the dialog"), + break("application a is added in the 'Included' column", + "reset configuration"), + break("we are back to default - no included and no excluded applications", + "undo"), + break("a warning dialog is displayed, with only an ok button", + "click ok"), + break("a, kernel, stdlib and sasl are included and all other are excluded", + "select application a from 'Included' column and click red cross to " + "exclude it"), + break("application a is moved to 'Excluded' column", + "select application tools from 'Excluded' column and click green V to " + "include it"), + break("application tools is moved to 'Included' column", + "select application runtime-tools from 'Excluded' column and click " + "green V to include it"), + break("application runtime-tools is moved to 'Included' column", + "undo"), + + ExplicitConfig = filename:join(PrivDir,"explicit.config"), + break("application runtime-tools is moved back to 'Excluded' column", + {"save configuration as 'explicit' to ~p",[ExplicitConfig]}), + ExpectedExplicitConfig = + {sys,[{lib_dirs,[filename:join(DataDir,"faulty_app_file")]}, + {incl_cond,exclude}, + {app,a,[{incl_cond,exclude}]}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,tools,[{incl_cond,include}]}]}, + check_config(ExpectedExplicitConfig,ExplicitConfig), + + break("The saved configuration file is checked and is ok.\n" + "Now go to the 'Libraries' tab and change the root directory to " + "some invalid directory."), + break("an error dialog occurs saying that there is no lib dir", + {"add library directory ~p, and click 'ok' in warning dialog", + [filename:join(DataDir,"dependencies")]}), + break("applications x, y and z are added to the 'Excluded' column in " + "'Applications' tab", + "go to the 'System settings' tab and set application inclusion policy " + "to 'derived'"), + break("a is excluded, kernel, stdlib, sasl and tools are included and " + "x, y and z are available in the 'Applications' tab", + "undo, and click ok in the warning dialog"), + break("all non included application are moved to the 'Excluded' column " + "and that 'Application inclusion policy' under 'System settings' " + "tab is set back to 'exclude'", + "undo again"), + break("applications x, y and z are in the 'Available' column", + "open application x, go to the 'Application settings' tab and set " + "'Application inclusion policy' to 'Use application specific config' " + "and 'include'. Close the window"), + break("application x is moved to the 'Included' column and z and y are moved " + "to the 'Derived' column in the system window", + "open application y"), + break("modules y1, y2 and y3 are in the 'Derived' column", + "go to 'Application dependencies' tab"), + break("application y is used by x, requires kernel and stdlib, and uses z", + "go to 'Module dependencies' tab"), + break("y is used by x2 and x3, and uses z1", + "got to 'Application settings' tab and select " + "'Source selection policy' 'Use selected version'"), + break("'Directories' frame becomes enabled (clickable)", + "select 'Module inclusion policy' 'Use application specific config' " + "and select 'app file + derived'"), + break("module y3 is moved to the 'Available' column in the 'Modules' tab", + "open module y1"), + break("module y1 is used by x2 and uses z1", + "go to the 'Code' tab and double click the line with call to z1:f()"), + break("new tab is opened with module z1", + "find something"), + break("it is found", + "goto some line"), + break("the cursor is moved to that line", + "click the 'Back' button"), + break("the cursor is moved back to the line of your previous 'find'", + "terminate the application window (but keep the module window)."), + + {ok,ServerPid} = reltool:get_server(SysPid), + unlink(SysPid), + break("the system window is still alive", + "terminate reltool by hitting 'Ctrl-q' when system window is active"), + false = erlang:is_process_alive(SysPid), + false = erlang:is_process_alive(ServerPid), + + break("Check that both module window and system window are terminated"), + + ok. + + +depgraphs(Config) -> + PrivDir = ?config(priv_dir,Config), + SimpleConfigFile = create_simple_config(PrivDir), + {ok, SysPid} = ?msym({ok, _}, reltool:start([{config,SimpleConfigFile}])), + link(SysPid), + + break("Open the application dependency graph and \n\n" + "*move the complete graph by left clicking somewhere over it and drag\n" + "*move one node left clicking the node and drag\n" + "*lock node to position by holding down shift while releasing\n" + "*select several nodes with ctrl and left mouse button\n" + "*lock/unlock selected nodes with suitable buttons\n" + "*freeze\n" + "*reset\n" + "*left slider: push nodes apart\n" + "*right slider: pull nodes together\n" + "*middle slider: adjust length of links\n" + "*select node and delete\n"), + break("Open the module dependency graph and meditate over it... "), + + unlink(SysPid), + break("Terminate reltool from the file menu in the system window"), + false = erlang:is_process_alive(SysPid), + + break("Check that system window and graphs are terminated"), + + ok. + + + + +%%%----------------------------------------------------------------- +%%% Internal functions +break(CheckStr,DoStr) when is_list(CheckStr), is_list(DoStr) -> + Str = io_lib:format("Check that ~s.~n~nThen ~s.",[CheckStr,DoStr]), + break(Str); +break(Check,Do) -> + CheckStr = + case Check of + {CheckFormat,CheckArgs} -> io_lib:format(CheckFormat,CheckArgs); + _ -> Check + end, + DoStr = + case Do of + {DoFormat,DoArgs} -> io_lib:format(DoFormat,DoArgs); + _ -> Do + end, + break(CheckStr,DoStr). + +break(Str) -> + test_server:break(Str). + +check_config(Expected,File) -> + {ok,[Config]} = file:consult(File), + ?m(Expected,Config). + +create_simple_config(PrivDir) -> + SimpleConfigFile = filename:join(PrivDir,"simple.config"), + SimpleConfig = {sys,[{incl_cond,exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}]}, + ok=file:write_file(SimpleConfigFile,io_lib:format("~p.~n",[SimpleConfig])), + SimpleConfigFile. + +create_warning_config(PrivDir,DataDir) -> + WarningConfigFile = filename:join(PrivDir,"warning.config"), + FaultyAppFileDir = filename:join(DataDir,"faulty_app_file"), + WarningConfig = {sys,[{lib_dirs,[FaultyAppFileDir]}, + {incl_cond,exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,a,[{incl_cond,include}]} + ]}, + ok=file:write_file(WarningConfigFile,io_lib:format("~p.~n",[WarningConfig])), + WarningConfigFile. diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/Makefile.src b/lib/reltool/test/reltool_manual_gui_SUITE_data/Makefile.src new file mode 100644 index 0000000000..9a340274ad --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/Makefile.src @@ -0,0 +1,28 @@ +EFLAGS=+debug_info + +DEPENDENCIES= \ + dependencies/x-1.0/ebin/x1.@EMULATOR@ \ + dependencies/x-1.0/ebin/x2.@EMULATOR@ \ + dependencies/x-1.0/ebin/x3.@EMULATOR@ \ + dependencies/y-1.0/ebin/y1.@EMULATOR@ \ + dependencies/y-1.0/ebin/y2.@EMULATOR@ \ + dependencies/y-1.0/ebin/y3.@EMULATOR@ \ + dependencies/z-1.0/ebin/z1.@EMULATOR@ + + +all: $(DEPENDENCIES) + +dependencies/x-1.0/ebin/x1.@EMULATOR@: dependencies/x-1.0/src/x1.erl + erlc $(EFLAGS) -odependencies/x-1.0/ebin dependencies/x-1.0/src/x1.erl +dependencies/x-1.0/ebin/x2.@EMULATOR@: dependencies/x-1.0/src/x2.erl + erlc $(EFLAGS) -odependencies/x-1.0/ebin dependencies/x-1.0/src/x2.erl +dependencies/x-1.0/ebin/x3.@EMULATOR@: dependencies/x-1.0/src/x3.erl + erlc $(EFLAGS) -odependencies/x-1.0/ebin dependencies/x-1.0/src/x3.erl +dependencies/y-1.0/ebin/y1.@EMULATOR@: dependencies/y-1.0/src/y1.erl + erlc $(EFLAGS) -odependencies/y-1.0/ebin dependencies/y-1.0/src/y1.erl +dependencies/y-1.0/ebin/y2.@EMULATOR@: dependencies/y-1.0/src/y2.erl + erlc $(EFLAGS) -odependencies/y-1.0/ebin dependencies/y-1.0/src/y2.erl +dependencies/y-1.0/ebin/y3.@EMULATOR@: dependencies/y-1.0/src/y3.erl + erlc $(EFLAGS) -odependencies/y-1.0/ebin dependencies/y-1.0/src/y3.erl +dependencies/z-1.0/ebin/z1.@EMULATOR@: dependencies/z-1.0/src/z1.erl + erlc $(EFLAGS) -odependencies/z-1.0/ebin dependencies/z-1.0/src/z1.erl diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/ebin/x.app b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/ebin/x.app new file mode 100644 index 0000000000..ccaab8a8c7 --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/ebin/x.app @@ -0,0 +1,7 @@ +% -*-erlang-*- +{application, x, + [{description, "Main application in reltool dependency test"}, + {vsn, "1.0"}, + {modules, [x1,x2,x3]}, + {registered, []}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/src/x1.erl b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/src/x1.erl new file mode 100644 index 0000000000..bf1e7f9279 --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/src/x1.erl @@ -0,0 +1,5 @@ +-module(x1). +-compile(export_all). + +f() -> + ok. diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/src/x2.erl b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/src/x2.erl new file mode 100644 index 0000000000..82191ba278 --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/src/x2.erl @@ -0,0 +1,5 @@ +-module(x2). +-compile(export_all). + +f() -> + y1:f(). diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/src/x3.erl b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/src/x3.erl new file mode 100644 index 0000000000..618c75c9a7 --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/src/x3.erl @@ -0,0 +1,5 @@ +-module(x3). +-compile(export_all). + +f() -> + y2:f(). diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/ebin/y.app b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/ebin/y.app new file mode 100644 index 0000000000..4f9b610b69 --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/ebin/y.app @@ -0,0 +1,7 @@ +% -*-erlang-*- +{application, y, + [{description, "Library application in reltool dependency test"}, + {vsn, "1.0"}, + {modules, [y1,y2]}, % y3 is skipped on purpose - to test module inclusion policy + {registered, []}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/src/y1.erl b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/src/y1.erl new file mode 100644 index 0000000000..dd21b33292 --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/src/y1.erl @@ -0,0 +1,5 @@ +-module(y1). +-compile(export_all). + +f() -> + z1:f(). diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/src/y2.erl b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/src/y2.erl new file mode 100644 index 0000000000..bf8ddf6080 --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/src/y2.erl @@ -0,0 +1,5 @@ +-module(y2). +-compile(export_all). + +f() -> + ok. diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/src/y3.erl b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/src/y3.erl new file mode 100644 index 0000000000..915d64d5b9 --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/src/y3.erl @@ -0,0 +1,5 @@ +-module(y3). +-compile(export_all). + +f() -> + ok. diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/z-1.0/ebin/z.app b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/z-1.0/ebin/z.app new file mode 100644 index 0000000000..437a0968e9 --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/z-1.0/ebin/z.app @@ -0,0 +1,7 @@ +% -*-erlang-*- +{application, z, + [{description, "Library application in reltool dependency test"}, + {vsn, "1.0"}, + {modules, [z1]}, + {registered, []}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/z-1.0/src/z1.erl b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/z-1.0/src/z1.erl new file mode 100644 index 0000000000..97ef90b87f --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/z-1.0/src/z1.erl @@ -0,0 +1,5 @@ +-module(z1). +-compile(export_all). + +f() -> + ok. diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/faulty_app_file/a-1.0/ebin/a.app b/lib/reltool/test/reltool_manual_gui_SUITE_data/faulty_app_file/a-1.0/ebin/a.app new file mode 100644 index 0000000000..ea77103598 --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/faulty_app_file/a-1.0/ebin/a.app @@ -0,0 +1 @@ +faulty app file diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/faulty_app_file/a-1.0/src/a.erl b/lib/reltool/test/reltool_manual_gui_SUITE_data/faulty_app_file/a-1.0/src/a.erl new file mode 100644 index 0000000000..bb500bed69 --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/faulty_app_file/a-1.0/src/a.erl @@ -0,0 +1,49 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(a). + + +-behaviour(gen_server). + +-vsn(1). + +%% External exports +-export([start_link/0, a/0]). +%% Internal exports +-export([init/1, handle_call/3, handle_info/2, terminate/2]). + +start_link() -> gen_server:start_link({local, aa}, a, [], []). + +a() -> gen_server:call(aa, a). + +%%----------------------------------------------------------------- +%% Callback functions from gen_server +%%----------------------------------------------------------------- +init([]) -> + process_flag(trap_exit, true), + {ok, state}. + +handle_call(a, _From, State) -> + X = application:get_all_env(a), + {reply, X, State}. + +handle_info(_, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/faulty_app_file/a-1.0/src/a_sup.erl b/lib/reltool/test/reltool_manual_gui_SUITE_data/faulty_app_file/a-1.0/src/a_sup.erl new file mode 100644 index 0000000000..a141c1767b --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/faulty_app_file/a-1.0/src/a_sup.erl @@ -0,0 +1,37 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(a_sup). + + +-behaviour(supervisor). + +%% External exports +-export([start/2]). + +%% Internal exports +-export([init/1]). + +start(_, _) -> + supervisor:start_link({local, a_sup}, a_sup, []). + +init([]) -> + SupFlags = {one_for_one, 4, 3600}, + Config = {a, + {a, start_link, []}, + permanent, 2000, worker, [a]}, + {ok, {SupFlags, [Config]}}. diff --git a/lib/reltool/test/reltool_wx_SUITE.erl b/lib/reltool/test/reltool_wx_SUITE.erl index 51f433f206..13d71f4fd6 100644 --- a/lib/reltool/test/reltool_wx_SUITE.erl +++ b/lib/reltool/test/reltool_wx_SUITE.erl @@ -61,15 +61,46 @@ start_all_windows(TestInfo) when is_atom(TestInfo) -> reltool_test_lib:tc_info(TestInfo); start_all_windows(_Config) -> {ok, SysPid} = ?msym({ok, _}, reltool:start([{trap_exit, false}])), + erlang:monitor(process,SysPid), {ok, AppPid} = ?msym({ok, _}, reltool_sys_win:open_app(SysPid, stdlib)), - ?msym({ok, _}, reltool_app_win:open_mod(AppPid, escript)), + erlang:monitor(process,AppPid), + {ok, ModPid} = ?msym({ok, _}, reltool_app_win:open_mod(AppPid, escript)), + erlang:monitor(process,ModPid), + + %% Let all windows get started + timer:sleep(timer:seconds(10)), %% Test that server pid can be fetched, and that server is alive {ok, Server} = ?msym({ok,_}, reltool:get_server(SysPid)), ?m(true, erlang:is_process_alive(Server)), ?m({ok,{sys,[]}}, reltool:get_config(Server)), - timer:sleep(timer:seconds(10)), + %% Terminate + check_no_win_crash(), ?m(ok, reltool:stop(SysPid)), - + wait_terminate([{sys,SysPid},{app,AppPid},{mod,ModPid}]), + ok. + + +%%%----------------------------------------------------------------- +%%% Internal functions +check_no_win_crash() -> + receive {'DOWN',_,_,_,_} = Down -> + ct:log("Unexpected termination of window:~n~p",[Down]), + ct:fail("window crashed") + after 0 -> + ok + end. + +wait_terminate([]) -> + ok; +wait_terminate([{Win,P}|Rest]) -> + receive + {'DOWN',_,process,P,shutdown} -> + wait_terminate(Rest); + {'DOWN',_,process,P,Reason} -> + ct:log("~p window terminated with unexpected reason:~n~p", + [Win,Reason]), + ct:fail("unexpected exit reason from window") + end. -- cgit v1.2.3 From 523573110b7afc1491c1e67865303da2468bcf0f Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 30 Jan 2012 14:43:06 +0100 Subject: [reltool] Add tests for sorting of applications in .rel and .script files OTP-9794 Test cases create_release_sort and create_script_sort are added. The test are temporarily skipped since they detected quite a few bugs that will be corrected with OTP-9792. The following bug is corrected in this commit:' reltool_server did not recognize {App,InclApps} inside a 'rel' specification in the reltool config, e.g. {rel, "myrel", "1.0", [{myapp,[app2]}]}. --- lib/reltool/src/reltool_server.erl | 6 +- lib/reltool/test/reltool_server_SUITE.erl | 345 ++++++++++++++++++++- .../sort_apps/x-1.0/ebin/x.app | 7 + .../sort_apps/y-1.0/ebin/y.app | 7 + .../sort_apps/z-1.0/ebin/z.app | 8 + 5 files changed, 363 insertions(+), 10 deletions(-) create mode 100644 lib/reltool/test/reltool_server_SUITE_data/sort_apps/x-1.0/ebin/x.app create mode 100644 lib/reltool/test/reltool_server_SUITE_data/sort_apps/y-1.0/ebin/y.app create mode 100644 lib/reltool/test/reltool_server_SUITE_data/sort_apps/z-1.0/ebin/z.app (limited to 'lib') diff --git a/lib/reltool/src/reltool_server.erl b/lib/reltool/src/reltool_server.erl index 7dc2d21158..fcd1ad393e 100644 --- a/lib/reltool/src/reltool_server.erl +++ b/lib/reltool/src/reltool_server.erl @@ -1510,11 +1510,11 @@ decode(#rel{rel_apps = RelApps} = Rel, [RelApp | KeyVals], Status) -> case RelApp of Name when is_atom(Name) -> {true, #rel_app{name = Name}}; - {Name, Type} when is_atom(Name) -> - {is_type(Type), #rel_app{name = Name, app_type = Type}}; {Name, InclApps} when is_atom(Name), is_list(InclApps) -> VI = lists:all(fun erlang:is_atom/1, InclApps), {VI, #rel_app{name = Name, incl_apps = InclApps}}; + {Name, Type} when is_atom(Name) -> + {is_type(Type), #rel_app{name = Name, app_type = Type}}; {Name, Type, InclApps} when is_atom(Name), is_list(InclApps) -> VT = is_type(Type), VI = lists:all(fun erlang:is_atom/1, InclApps), @@ -1606,7 +1606,7 @@ check_app({RelName, AppName}, Apps, Status) -> Status; _ -> Text = lists:concat(["Release ", RelName, - " uses non included application ", + " uses non included application ", AppName]), reltool_utils:return_first_error(Status, Text) end. diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl index fc2d844d07..0aca242d4e 100644 --- a/lib/reltool/test/reltool_server_SUITE.erl +++ b/lib/reltool/test/reltool_server_SUITE.erl @@ -52,13 +52,32 @@ end_per_testcase(Func,Config) -> suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [start_server, set_config, get_config, create_release, - create_script, create_target, create_embedded, - create_standalone, create_old_target, eval_target_spec, - otp_9135, otp_9229_exclude_app, otp_9229_exclude_mod, - get_apps, get_mod, get_sys, set_app_and_undo, set_apps_and_undo, - set_sys_and_undo, load_config_and_undo, reset_config_and_undo, - gen_rel_files, save_config, dependencies]. + [start_server, + set_config, + get_config, + create_release, + create_release_sort, + create_script, + create_script_sort, + create_target, + create_embedded, + create_standalone, + create_old_target, + eval_target_spec, + otp_9135, + otp_9229_exclude_app, + otp_9229_exclude_mod, + get_apps, + get_mod, + get_sys, + set_app_and_undo, + set_apps_and_undo, + set_sys_and_undo, + load_config_and_undo, + reset_config_and_undo, + gen_rel_files, + save_config, + dependencies]. groups() -> []. @@ -250,6 +269,121 @@ create_release(_Config) -> ?m({ok, Rel}, reltool:get_rel([{config, Config}], RelName)), ok. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Generate releases and make sure order of applications specified in +%% 'rel' parameter is preserved and that included applications are +%% started before the including application. +%% Circular dependencies shall also be detected and cause error. + +create_release_sort(_Config) -> {skip, "Multiple known problems - see OTP-9792"}; +create_release_sort(Config) -> + DataDir = ?config(data_dir,Config), + %% Configure the server + RelName1 = "MnesiaFirst", + RelName2 = "SaslFirst", + RelName3 = "Include-both", + RelName4 = "Include-only-app", + RelName5 = "Include-only-rel", + RelName6 = "Include-missing-app", + RelName7 = "Circular", + RelName8 = "Include-both-missing-app", + RelName9 = "Include-overwrite", + RelVsn = "1.0", + Sys = + {sys, + [ + {lib_dirs, [filename:join(DataDir,"sort_apps")]}, + {boot_rel, RelName1}, + {rel, RelName1, RelVsn, [stdlib, kernel, mnesia, sasl]}, + {rel, RelName2, RelVsn, [stdlib, kernel, sasl, mnesia]}, + {rel, RelName3, RelVsn, [stdlib, kernel, {z,[tools]}, tools]}, + {rel, RelName4, RelVsn, [stdlib, kernel, z, tools]}, + {rel, RelName5, RelVsn, [stdlib, kernel, {sasl,[tools]}]}, + {rel, RelName6, RelVsn, [stdlib, kernel, z]}, %z includes tools in .app + {rel, RelName7, RelVsn, [stdlib, kernel, mnesia, y, sasl, x]}, + {rel, RelName8, RelVsn, [stdlib, kernel, {z,[tools]}]}, + {rel, RelName9, RelVsn, [stdlib, kernel, {z,[]}]}, + {incl_cond,exclude}, + {mod_cond,app}, + {app,kernel,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,mnesia,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,x,[{incl_cond,include}]}, + {app,y,[{incl_cond,include}]}, + {app,z,[{incl_cond,include}]}, + {app,tools,[{mod_cond,app},{incl_cond,include}]} + ]}, + %% Generate release + + %% BUG: reltool reverses the list of applications after kernel and stdlib + ?msym({ok, {release, {RelName1, RelVsn}, + {erts, _}, + [{kernel, _}, + {stdlib, _}, + {mnesia, _}, + {sasl, _}]}}, + reltool:get_rel([{config, Sys}], RelName1)), + + %% BUG: reltool reverses the list of applications after kernel and stdlib + ?msym({ok, {release, {RelName2, RelVsn}, + {erts, _}, + [{kernel, _}, + {stdlib, _}, + {sasl, _}, + {mnesia, _}]}}, + reltool:get_rel([{config, Sys}], RelName2)), + + ?msym({ok, {release, {RelName3, RelVsn}, + {erts, _}, + [{kernel, _}, + {stdlib, _}, + {tools, _}, + {z, _, [tools]}]}}, + reltool:get_rel([{config, Sys}], RelName3)), + + %% BUG: reltool does not honor included applications in .app files + %% unless they are also mentioned in the 'rel' specification in + %% the reltool config. + %% => order of tools and z does not become correct in rel (tools + %% should be first since it is included in z) + ?msym({ok, {release, {RelName4, RelVsn}, + {erts, _}, + [{kernel, _}, + {stdlib, _}, + {tools, _}, + {z, _}]}}, + reltool:get_rel([{config, Sys}], RelName4)), + + ?m({error,"sasl: These applications are used by release " + "Include-only-rel but are missing as included_applications " + "in the app file: [tools]"}, + reltool:get_rel([{config, Sys}], RelName5)), + + %% BUG: reltool does not honor included applications in .app files + %% unless they are also mentioned in the 'rel' specification in + %% the reltool config. + %% => does not detect that tools (included in z) is missing + ?m({error, "Undefined applications: [tools]"}, + reltool:get_rel([{config, Sys}], RelName6)), + + ?m({error,"Circular dependencies: [x,y]"}, + reltool:get_rel([{config, Sys}], RelName7)), + + ?m({error,"Undefined applications: [tools]"}, + reltool:get_rel([{config, Sys}], RelName8)), + + %% BUG: Reltool looses the empty include list for z, which should + %% overwrite included_applications statement from the .app file. + ?msym({ok,{release,{RelName9,RelVsn}, + {erts,_}, + [{kernel,_}, + {stdlib,_}, + {z,_,[]}]}}, + reltool:get_rel([{config, Sys}], RelName9)), + + ok. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Generate boot scripts @@ -298,6 +432,203 @@ create_script(_Config) -> ?m(ok, reltool:stop(Pid)), ok. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Test creation of .script with different sorting of applications and +%% included applications. +%% Test that result is equal to what systools produces +create_script_sort(_Config) -> {skip, "Multiple known problems - see OTP-9792"}; +create_script_sort(Config) -> + DataDir = ?config(data_dir,Config), + %% Configure the server + RelName1 = "MnesiaFirst", + RelName2 = "SaslFirst", + RelName3 = "Include-both", + RelName4 = "Include-only-app", + RelName5 = "Include-only-rel", + RelName6 = "Include-missing-app", + RelName7 = "Circular", + RelName8 = "Include-both-missing-app", + RelName9 = "Include-overwrite", + RelVsn = "1.0", + LibDir = filename:join(DataDir,"sort_apps"), + Sys = + {sys, + [ + {lib_dirs, [LibDir]}, + {boot_rel, RelName1}, + {rel, RelName1, RelVsn, [stdlib, kernel, mnesia, sasl]}, + {rel, RelName2, RelVsn, [stdlib, kernel, sasl, mnesia]}, + {rel, RelName3, RelVsn, [stdlib, kernel, {z,[tools]}, tools]}, + {rel, RelName4, RelVsn, [stdlib, kernel, z, tools]}, + {rel, RelName5, RelVsn, [stdlib, kernel, {sasl,[tools]}]}, + {rel, RelName6, RelVsn, [stdlib, kernel, z]}, %z includes tools in .app + {rel, RelName7, RelVsn, [stdlib, kernel, mnesia, y, sasl, x]}, + {rel, RelName8, RelVsn, [stdlib, kernel, {z,[tools]}]}, + {rel, RelName9, RelVsn, [stdlib, kernel, {z,[]}]}, + {incl_cond,exclude}, + {mod_cond,app}, + {app,kernel,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,mnesia,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,x,[{incl_cond,include}]}, + {app,y,[{incl_cond,include}]}, + {app,z,[{incl_cond,include}]}, + {app,tools,[{mod_cond,app},{incl_cond,include}]} + ]}, + + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + + %% Generate release files + application:load(sasl), + application:load(mnesia), + application:load(tools), + {ok,KernelVsn} = application:get_key(kernel,vsn), + {ok,StdlibVsn} = application:get_key(stdlib,vsn), + {ok,SaslVsn} = application:get_key(sasl,vsn), + {ok,MnesiaVsn} = application:get_key(mnesia,vsn), + {ok,ToolsVsn} = application:get_key(tools,vsn), + ErtsVsn = erlang:system_info(version), + + Rel1 = {release, {RelName1,RelVsn}, {erts,ErtsVsn}, + [{kernel,KernelVsn}, + {stdlib,StdlibVsn}, + {mnesia,MnesiaVsn}, + {sasl,SaslVsn}]}, + FullName1 = filename:join(?WORK_DIR,RelName1), + ?m(ok, file:write_file(FullName1 ++ ".rel", io_lib:format("~p.\n", [Rel1]))), + Rel2 = {release, {RelName2,RelVsn}, {erts,ErtsVsn}, + [{kernel,KernelVsn}, + {stdlib,StdlibVsn}, + {sasl,SaslVsn}, + {mnesia,MnesiaVsn}]}, + FullName2 = filename:join(?WORK_DIR,RelName2), + ?m(ok, file:write_file(FullName2 ++ ".rel", io_lib:format("~p.\n", [Rel2]))), + Rel3 = {release, {RelName3,RelVsn}, {erts,ErtsVsn}, + [{kernel,KernelVsn}, + {stdlib,StdlibVsn}, + {z,"1.0",[tools]}, + {tools,ToolsVsn}]}, + FullName3 = filename:join(?WORK_DIR,RelName3), + ?m(ok, file:write_file(FullName3 ++ ".rel", io_lib:format("~p.\n", [Rel3]))), + Rel4 = {release, {RelName4,RelVsn}, {erts,ErtsVsn}, + [{kernel,KernelVsn}, + {stdlib,StdlibVsn}, + {z,"1.0"}, + {tools,ToolsVsn}]}, + FullName4 = filename:join(?WORK_DIR,RelName4), + ?m(ok, file:write_file(FullName4 ++ ".rel", io_lib:format("~p.\n", [Rel4]))), + Rel5 = {release, {RelName5,RelVsn}, {erts,ErtsVsn}, + [{kernel,KernelVsn}, + {stdlib,StdlibVsn}, + {sasl,SaslVsn,[tools]}, + {tools,ToolsVsn}]}, + FullName5 = filename:join(?WORK_DIR,RelName5), + ?m(ok, file:write_file(FullName5 ++ ".rel", io_lib:format("~p.\n", [Rel5]))), + Rel6 = {release, {RelName6,RelVsn}, {erts,ErtsVsn}, + [{kernel,KernelVsn}, + {stdlib,StdlibVsn}, + {z,"1.0"}]}, + FullName6 = filename:join(?WORK_DIR,RelName6), + ?m(ok, file:write_file(FullName6 ++ ".rel", io_lib:format("~p.\n", [Rel6]))), + Rel7 = {release, {RelName7,RelVsn}, {erts,ErtsVsn}, + [{kernel,KernelVsn}, + {stdlib,StdlibVsn}, + {mnesia,MnesiaVsn}, + {y,"1.0"}, + {sasl,SaslVsn}, + {x,"1.0"}]}, + FullName7 = filename:join(?WORK_DIR,RelName7), + ?m(ok, file:write_file(FullName7 ++ ".rel", io_lib:format("~p.\n", [Rel7]))), + Rel8 = {release, {RelName8,RelVsn}, {erts,ErtsVsn}, + [{kernel,KernelVsn}, + {stdlib,StdlibVsn}, + {z,"1.0",[tools]}]}, + FullName8 = filename:join(?WORK_DIR,RelName8), + ?m(ok, file:write_file(FullName8 ++ ".rel", io_lib:format("~p.\n", [Rel8]))), + Rel9 = {release, {RelName9,RelVsn}, {erts,ErtsVsn}, + [{kernel,KernelVsn}, + {stdlib,StdlibVsn}, + {z,"1.0",[]}]}, + FullName9 = filename:join(?WORK_DIR,RelName9), + ?m(ok, file:write_file(FullName9 ++ ".rel", io_lib:format("~p.\n", [Rel9]))), + + %% Generate script files with systools and reltool and compare + ZPath = filename:join([LibDir,"*",ebin]), + + %% BUG: reltool reverses the list of applications after kernel and stdlib + %% => mnesia and sasl are reverted + ?msym({ok,_,_}, systools_make_script(FullName1,ZPath)), + {ok, [SystoolsScript1]} = ?msym({ok,[_]}, file:consult(FullName1++".script")), + {ok, Script1} = ?msym({ok, _}, reltool:get_script(Pid, RelName1)), + ?m(equal, diff_script(SystoolsScript1, Script1)), + + %% BUG: reltool reverses the list of applications after kernel and stdlib + %% => mnesia and sasl are reverted + ?msym({ok,_,_}, systools_make_script(FullName2,ZPath)), + {ok, [SystoolsScript2]} = ?msym({ok,[_]}, file:consult(FullName2++".script")), + {ok, Script2} = ?msym({ok, _}, reltool:get_script(Pid, RelName2)), + ?m(equal, diff_script(SystoolsScript2, Script2)), + + %% BUG1: reltool loads all modules in the ebin dir of an application, + %% even if mod_cond is set to 'app'. + %% BUG2: reltool shall not start included applications!! + ?msym({ok,_,_}, systools_make_script(FullName3,ZPath)), + {ok, [SystoolsScript3]} = ?msym({ok,[_]}, file:consult(FullName3++".script")), + {ok, Script3} = ?msym({ok, _}, reltool:get_script(Pid, RelName3)), + ?m(equal, diff_script(SystoolsScript3, Script3)), + + %% BUG1: reltool loads all modules in the ebin dir of an application, + %% even if mod_cond is set to 'app'. + %% BUG2: reltool does not honor included applications in .app files + %% unless they are also mentioned in the 'rel' specification in + %% the reltool config. + %% => faulty order of load instructions for tools and z. tools + %% should be first since it is included in z. + ?msym({ok,_,_}, systools_make_script(FullName4,ZPath)), + {ok, [SystoolsScript4]} = ?msym({ok,[_]}, file:consult(FullName4++".script")), + {ok, Script4} = ?msym({ok, _}, reltool:get_script(Pid, RelName4)), + ?m(equal, diff_script(SystoolsScript4, Script4)), + + ?msym({error,_,[{error_reading,{sasl,{override_include,_}}}]}, + systools_make_script(FullName5,ZPath)), + ?m({error,"sasl: These applications are used by release " + "Include-only-rel but are missing as included_applications " + "in the app file: [tools]"}, + reltool:get_script(Pid, RelName5)), + + %% BUG: reltool does not honor included applications in .app files + %% unless they are also mentioned in the 'rel' specification in + %% the reltool config. + %% => does not detect that tools (included in z) is missing + ?msym({error,_,{undefined_applications,_}}, + systools_make_script(FullName6,ZPath)), + ?m({error, "Undefined applications: [tools]"}, + reltool:get_script(Pid, RelName6)), + + ?msym({error,_,{circular_dependencies,_}}, + systools_make_script(FullName7,ZPath)), + ?m({error,"Circular dependencies: [x,y]"}, + reltool:get_script(Pid, RelName7)), + + ?msym({error,_,{undefined_applications,_}}, + systools_make_script(FullName8,ZPath)), + ?m({error, "Undefined applications: [tools]"}, + reltool:get_script(Pid, RelName8)), + + ?msym({ok,_,_}, systools_make_script(FullName9,ZPath)), + {ok, [SystoolsScript9]} = ?msym({ok,[_]}, file:consult(FullName9++".script")), + {ok, Script9} = ?msym({ok, _}, reltool:get_script(Pid, RelName9)), + ?m(equal, diff_script(SystoolsScript9, Script9)), + + %% Stop server + ?m(ok, reltool:stop(Pid)), + ok. + + +systools_make_script(Name,Path) -> + systools:make_script(Name,[{path,[Path]},{outdir,?WORK_DIR},silent]). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Generate target system diff --git a/lib/reltool/test/reltool_server_SUITE_data/sort_apps/x-1.0/ebin/x.app b/lib/reltool/test/reltool_server_SUITE_data/sort_apps/x-1.0/ebin/x.app new file mode 100644 index 0000000000..5fa2a92969 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/sort_apps/x-1.0/ebin/x.app @@ -0,0 +1,7 @@ +% -*-erlang-*- +{application, x, + [{description, "Application in reltool sort app test - circular dependency"}, + {vsn, "1.0"}, + {modules,[]}, + {registered, []}, + {applications, [kernel, stdlib, y]}]}. diff --git a/lib/reltool/test/reltool_server_SUITE_data/sort_apps/y-1.0/ebin/y.app b/lib/reltool/test/reltool_server_SUITE_data/sort_apps/y-1.0/ebin/y.app new file mode 100644 index 0000000000..c4bc62f55f --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/sort_apps/y-1.0/ebin/y.app @@ -0,0 +1,7 @@ +% -*-erlang-*- +{application, y, + [{description, "Application in reltool sort app test - circular dependency"}, + {vsn, "1.0"}, + {modules,[]}, + {registered, []}, + {applications, [kernel, stdlib, x]}]}. diff --git a/lib/reltool/test/reltool_server_SUITE_data/sort_apps/z-1.0/ebin/z.app b/lib/reltool/test/reltool_server_SUITE_data/sort_apps/z-1.0/ebin/z.app new file mode 100644 index 0000000000..1622975bf6 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/sort_apps/z-1.0/ebin/z.app @@ -0,0 +1,8 @@ +% -*-erlang-*- +{application, z, + [{description, "Application in reltool sort app test - included applications"}, + {vsn, "1.0"}, + {modules,[]}, + {registered, []}, + {applications, [kernel, stdlib]}, + {included_applications, [tools]}]}. -- cgit v1.2.3 From e3bb31270b1cc43a72a2c3942f496e5c8f93155b Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 13 Feb 2012 16:57:51 +0100 Subject: [reltool] Add test cases for handling of escripts OTP-9794 OTP-9968 The following test cases are added: * create_standalone_beam * create_standalone_app * create_multiple_standalone * load_config_escript_path * load_config_same_escript_source * load_config_same_escript_beam * load_config_add_escript Most of them are temporarily skipped since they re-produce known problems that will be corrected in a later commit. --- lib/reltool/test/reltool_server_SUITE.erl | 365 ++++++++++++++++++++- .../test/reltool_server_SUITE_data/Makefile.src | 8 +- .../escript/someapp-1.0/ebin/someapp.app | 6 + .../escript/someapp-1.0/src/mymod.erl | 26 ++ 4 files changed, 394 insertions(+), 11 deletions(-) create mode 100644 lib/reltool/test/reltool_server_SUITE_data/escript/someapp-1.0/ebin/someapp.app create mode 100644 lib/reltool/test/reltool_server_SUITE_data/escript/someapp-1.0/src/mymod.erl (limited to 'lib') diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl index 0aca242d4e..56c9cb5ed1 100644 --- a/lib/reltool/test/reltool_server_SUITE.erl +++ b/lib/reltool/test/reltool_server_SUITE.erl @@ -62,6 +62,9 @@ all() -> create_target, create_embedded, create_standalone, + create_standalone_beam, + create_standalone_app, + create_multiple_standalone, create_old_target, eval_target_spec, otp_9135, @@ -74,6 +77,10 @@ all() -> set_apps_and_undo, set_sys_and_undo, load_config_and_undo, + load_config_escript_path, + load_config_same_escript_source, + load_config_same_escript_beam, + load_config_add_escript, reset_config_and_undo, gen_rel_files, save_config, @@ -138,27 +145,34 @@ set_config(_Config) -> %% Check that get_config returns the expected derivates and defaults %% as specified get_config(_Config) -> + + KVsn = latest(kernel), + StdVsn = latest(stdlib), + SaslVsn = latest(sasl), + Sys = {sys,[{incl_cond, exclude}, {app,kernel,[{incl_cond,include}]}, - {app,sasl,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include},{vsn,SaslVsn}]}, {app,stdlib,[{incl_cond,include}]}]}, {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), ?m({ok, Sys}, reltool:get_config(Pid)), ?m({ok, Sys}, reltool:get_config(Pid,false,false)), + %% Include derived info ?msym({ok,{sys,[{incl_cond, exclude}, {erts,[]}, {app,kernel,[{incl_cond,include},{mod,_,[]}|_]}, - {app,sasl,[{incl_cond,include},{mod,_,[]}|_]}, + {app,sasl,[{incl_cond,include},{vsn,SaslVsn},{mod,_,[]}|_]}, {app,stdlib,[{incl_cond,include},{mod,_,[]}|_]}]}}, reltool:get_config(Pid,false,true)), + %% Include defaults ?msym({ok,{sys,[{root_dir,_}, {lib_dirs,_}, {mod_cond,all}, {incl_cond,exclude}, {app,kernel,[{incl_cond,include},{vsn,undefined}]}, - {app,sasl,[{incl_cond,include},{vsn,undefined}]}, + {app,sasl,[{incl_cond,include},{vsn,SaslVsn}]}, {app,stdlib,[{incl_cond,include},{vsn,undefined}]}, {boot_rel,"start_clean"}, {rel,"start_clean","1.0",[]}, @@ -178,16 +192,14 @@ get_config(_Config) -> {debug_info,keep}]}}, reltool:get_config(Pid,true,false)), - KVsn = latest(kernel), - StdVsn = latest(stdlib), - + %% Include both defaults and derived info ?msym({ok,{sys,[{root_dir,_}, {lib_dirs,_}, {mod_cond,all}, {incl_cond,exclude}, {erts,[]}, {app,kernel,[{incl_cond,include},{vsn,KVsn},{mod,_,[]}|_]}, - {app,sasl,[{incl_cond,include},{vsn,_},{mod,_,[]}|_]}, + {app,sasl,[{incl_cond,include},{vsn,SaslVsn},{mod,_,[]}|_]}, {app,stdlib,[{incl_cond,include},{vsn,StdVsn},{mod,_,[]}|_]}, {boot_rel,"start_clean"}, {rel,"start_clean","1.0",[]}, @@ -720,21 +732,187 @@ create_standalone(_Config) -> ?m(ok, file:make_dir(TargetDir)), ok = ?m(ok, reltool:create_target([{config, Config}], TargetDir)), + %% Start the target system and fetch root dir BinDir = filename:join([TargetDir, "bin"]), Erl = filename:join([BinDir, "erl"]), {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), RootDir = ?ignore(rpc:call(Node, code, root_dir, [])), ?msym(ok, stop_node(Node)), + %% Execute escript Expected = iolist_to_binary(["Root dir: ", RootDir, "\n" "Script args: [\"-arg1\",\"arg2\",\"arg3\"]\n", "Smp: false\n", "ExitCode:0"]), io:format("Expected: ~s\n", [Expected]), - ?m(Expected, run(BinDir, EscriptName ++ " -arg1 arg2 arg3")), + ?m(Expected, run(BinDir, EscriptName, "-arg1 arg2 arg3")), ok. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Generate standalone system with inlined beam file + +create_standalone_beam(Config) -> + %% Read beam file + DataDir = ?config(data_dir,Config), + BeamFile = filename:join([DataDir,escript,"someapp-1.0",ebin,"mymod.beam"]), + {ok,BeamBin} = file:read_file(BeamFile), + + %% Create the escript + EscriptName = "mymod.escript", + Escript = filename:join(?WORK_DIR,EscriptName), + ok = escript:create(Escript,[shebang,{beam,BeamBin}]), + ok = file:change_mode(Escript,8#00744), + + %% Configure the server + Sys = + {sys, + [ + {lib_dirs, []}, + {escript, Escript, [{incl_cond, include}]}, + {profile, standalone} + ]}, + + %% Generate target file + TargetDir = filename:join([?WORK_DIR, "target_standalone_beam"]), + ?m(ok, reltool_utils:recursive_delete(TargetDir)), + ?m(ok, file:make_dir(TargetDir)), + ok = ?m(ok, reltool:create_target([{config, Sys}], TargetDir)), + + %% Start the target system and fetch root dir + BinDir = filename:join([TargetDir, "bin"]), + Erl = filename:join([BinDir, "erl"]), + {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), + RootDir = ?ignore(rpc:call(Node, code, root_dir, [])), + ?msym(ok, stop_node(Node)), + + %% Execute escript + Expected = iolist_to_binary(["Root dir: ", RootDir, "\n" + "Script args: [\"-arg1\",\"arg2\",\"arg3\"]\n", + "ExitCode:0"]), + io:format("Expected: ~s\n", [Expected]), + ?m(Expected, run(BinDir, EscriptName, "-arg1 arg2 arg3")), + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% 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), + 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} + ]}, + + %% Generate target file + TargetDir = filename:join([?WORK_DIR, "target_standalone_app"]), + ?m(ok, reltool_utils:recursive_delete(TargetDir)), + ?m(ok, file:make_dir(TargetDir)), + ok = ?m(ok, reltool:create_target([{config, Sys}], TargetDir)), + + %% Start the target system and fetch root dir + BinDir = filename:join([TargetDir, "bin"]), + Erl = filename:join([BinDir, "erl"]), + {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), + RootDir = ?ignore(rpc:call(Node, code, root_dir, [])), + ?msym(ok, stop_node(Node)), + + %% Execute escript + Expected = iolist_to_binary(["Root dir: ", RootDir, "\n" + "Script args: [\"-arg1\",\"arg2\",\"arg3\"]\n", + "ExitCode:0"]), + io:format("Expected: ~s\n", [Expected]), + ?m(Expected, run(BinDir, EscriptName, "-arg1 arg2 arg3")), + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Generate standalone system with multiple escripts + +create_multiple_standalone(Config) -> + %% First escript + ExDir = code:lib_dir(reltool, examples), + EscriptName1 = "display_args", + Escript1 = filename:join([ExDir, EscriptName1]), + + %% Second escript + DataDir = ?config(data_dir,Config), + BeamFile = filename:join([DataDir,escript,"someapp-1.0",ebin,"mymod.beam"]), + {ok,BeamBin} = file:read_file(BeamFile), + EscriptName2 = "mymod.escript", + Escript2 = filename:join(?WORK_DIR,EscriptName2), + ok = escript:create(Escript2,[shebang,{beam,BeamBin}]), + ok = file:change_mode(Escript2,8#00744), + + %% Configure server + Sys = + {sys, + [ + {lib_dirs, []}, + {escript, Escript1, [{incl_cond, include}]}, + {escript, Escript2, [{incl_cond, include}]}, + {profile, standalone} + ]}, + + %% Generate target system + TargetDir = filename:join([?WORK_DIR, "target_multiple_standalone"]), + ?m(ok, reltool_utils:recursive_delete(TargetDir)), + ?m(ok, file:make_dir(TargetDir)), + ok = ?m(ok, reltool:create_target([{config,Sys}], TargetDir)), + + %% Start the target system and fetch root dir + BinDir = filename:join([TargetDir, "bin"]), + Erl = filename:join([BinDir, "erl"]), + {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), + RootDir = ?ignore(rpc:call(Node, code, root_dir, [])), + ?msym(ok, stop_node(Node)), + + %% Execute escript1 + Expected1 = iolist_to_binary(["Root dir: ", RootDir, "\n" + "Script args: [\"-arg1\",\"arg2\",\"arg3\"]\n", + "Smp: false\n", + "ExitCode:0"]), + io:format("Expected1: ~s\n", [Expected1]), + ?m(Expected1, run(BinDir, EscriptName1, "-arg1 arg2 arg3")), + + + %% Execute escript2 + Expected2 = iolist_to_binary(["Root dir: ", RootDir, "\n" + "Script args: [\"-arg1\",\"arg2\",\"arg3\"]\n", + "ExitCode:0"]), + io:format("Expected2: ~s\n", [Expected2]), + ?m(Expected2, run(BinDir, EscriptName2, "-arg1 arg2 arg3")), + + ok. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Generate old type of target system @@ -1182,6 +1360,171 @@ load_config_and_undo(Config) -> ok. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Load config with escript + +load_config_escript_path(_Config) -> {skip,"Known bug: loading config with escript at reltool start creates different #app record than loading same config with load_config"}; +load_config_escript_path(Config) -> + %% Create escript + DataDir = ?config(data_dir,Config), + BeamFile = filename:join([DataDir,escript,"someapp-1.0",ebin,"mymod.beam"]), + {ok,BeamBin} = file:read_file(BeamFile), + EscriptName = "mymod.escript", + Escript = filename:join(?WORK_DIR,EscriptName), + ok = escript:create(Escript,[shebang,{beam,BeamBin}]), + ok = file:change_mode(Escript,8#00744), + + %% Start reltool_server with one escript in configuration + EscriptSys = + {sys, + [ + {lib_dirs, []}, + {escript, Escript, [{incl_cond, include}]}, + {profile, standalone} + ]}, + + {ok, Pid1} = ?msym({ok, _}, reltool:start_server([{config, EscriptSys}])), + {ok,[#app{name='*escript* mymod'}=A]} = + ?msym({ok,[_]}, reltool_server:get_apps(Pid1,whitelist)), + ?m(ok, reltool:stop(Pid1)), + + + %% Do same again, but now start reltool first with simple config, + %% then add escript by loading new configuration and check that + %% #app is the same + SimpleSys = + {sys, + [ + {lib_dirs, []} + ]}, + + {ok, Pid2} = ?msym({ok, _}, reltool:start_server([{config, SimpleSys}])), + ?m({ok,[]}, reltool_server:get_apps(Pid2,whitelist)), + ?m({ok,[]}, reltool_server:load_config(Pid2,EscriptSys)), + ?m({ok,[A]}, reltool_server:get_apps(Pid2,whitelist)), + + ?m(ok, reltool:stop(Pid2)), + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Load config with same (source) escript twice and check that the +%% application information is not changed. + +load_config_same_escript_source(_Config) -> {skip,"Known bug: loading config with same escript (source) twice causes reltool to add same module twice in #app.mods"}; +load_config_same_escript_source(_Config) -> + %% Create escript + ExDir = code:lib_dir(reltool, examples), + EscriptName = "display_args", + Escript = filename:join([ExDir, EscriptName]), + + %% Start reltool_server with one escript in configuration + Sys = + {sys, + [ + {lib_dirs, []}, + {escript, Escript, [{incl_cond, include}]}, + {profile, standalone} + ]}, + + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + {ok,[#app{name='*escript* display_args'}=A]} = + ?msym({ok,[_]}, reltool_server:get_apps(Pid,whitelist)), + + %% Load the same config again, then check that app is not changed + ?m({ok,[]}, reltool_server:load_config(Pid,Sys)), + ?m({ok,[A]}, reltool_server:get_apps(Pid,whitelist)), + + ?m(ok, reltool:stop(Pid)), + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Load config with same (beam) escript twice and check that the +%% application information is not changed. + +load_config_same_escript_beam(_Config) -> {skip,"Known bug: loading config with same escript (with inlined beam) twice causes reltool to fail and say module is included by two different applications"}; +load_config_same_escript_beam(Config) -> + %% Create escript + DataDir = ?config(data_dir,Config), + BeamFile = filename:join([DataDir,escript,"someapp-1.0",ebin,"mymod.beam"]), + {ok,BeamBin} = file:read_file(BeamFile), + EscriptName = "mymod.escript", + Escript = filename:join(?WORK_DIR,EscriptName), + ok = escript:create(Escript,[shebang,{beam,BeamBin}]), + ok = file:change_mode(Escript,8#00744), + + %% Start reltool_server with one escript in configuration + Sys = + {sys, + [ + {lib_dirs, []}, + {escript, Escript, [{incl_cond, include}]}, + {profile, standalone} + ]}, + + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + {ok,[#app{name='*escript* mymod'}=A]} = + ?msym({ok,[_]}, reltool_server:get_apps(Pid,whitelist)), + + %% Load the same config again, then check that app is not changed + ?m({ok,[]}, reltool_server:load_config(Pid,Sys)), + ?m({ok,[A]}, reltool_server:get_apps(Pid,whitelist)), + + ?m(ok, reltool:stop(Pid)), + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Load config with escript + +%% BUG: see OTP-9792, 25) +load_config_add_escript(_Config) -> {skip,"Known bug: Can not load_config which in addition to an existing escript also adds another escript for which the name sorts before the existing one"}; +load_config_add_escript(Config) -> + %% First escript + ExDir = code:lib_dir(reltool, examples), + EscriptName1 = "display_args", + Escript1 = filename:join([ExDir, EscriptName1]), + + %% Second escript + DataDir = ?config(data_dir,Config), + BeamFile = filename:join([DataDir,escript,"someapp-1.0",ebin,"mymod.beam"]), + {ok,BeamBin} = file:read_file(BeamFile), + EscriptName2 = "mymod.escript", + Escript2 = filename:join(?WORK_DIR,EscriptName2), + ok = escript:create(Escript2,[shebang,{beam,BeamBin}]), + ok = file:change_mode(Escript2,8#00744), + + %% Start reltool_server with one escript in configuration + Sys1 = + {sys, + [ + {lib_dirs, []}, + {escript, Escript2, [{incl_cond, include}]}, + {profile, standalone} + ]}, + + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys1}])), + + %% Add second escript by loading new configuration + Sys2 = + {sys, + [ + {lib_dirs, []}, + {escript, Escript1, [{incl_cond, include}]}, + {escript, Escript2, [{incl_cond, include}]}, + {profile, standalone} + ]}, + + {ok,[]} = ?m({ok,[]}, reltool_server:load_config(Pid,Sys2)), + {ok,[#app{name='*escript* display_args'}, + #app{name='*escript* mymod'}]} = + ?msym({ok,[_,_]}, reltool_server:get_apps(Pid,whitelist)), + + ?m(ok, reltool:stop(Pid)), + + ok. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% reset_config_and_undo(Config) -> Sys1 = {sys,[{lib_dirs,[filename:join(datadir(Config),"faulty_app_file")]}, @@ -1654,14 +1997,16 @@ wait_for_process(Node, Name, N) when is_integer(N), N > 0 -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Run escript -run(Dir, Cmd0) -> +run(Dir, Script, Args) -> + Cmd0 = filename:rootname(Script) ++ " " ++ Args, Cmd = case os:type() of {win32,_} -> filename:nativename(Dir) ++ "\\" ++ Cmd0; _ -> Cmd0 end, do_run(Dir, Cmd). -run(Dir, Opts, Cmd0) -> +run(Dir, Opts, Script, Args) -> + Cmd0 = filename:rootname(Script) ++ " " ++ Args, Cmd = case os:type() of {win32,_} -> Opts ++ " " ++ filename:nativename(Dir) ++ "\\" ++ Cmd0; _ -> Opts ++ " " ++ Dir ++ "/" ++ Cmd0 diff --git a/lib/reltool/test/reltool_server_SUITE_data/Makefile.src b/lib/reltool/test/reltool_server_SUITE_data/Makefile.src index 35a239bfe2..8ab077d64b 100644 --- a/lib/reltool/test/reltool_server_SUITE_data/Makefile.src +++ b/lib/reltool/test/reltool_server_SUITE_data/Makefile.src @@ -14,8 +14,11 @@ DEPENDENCIES= \ dependencies/y-1.0/ebin/y2.@EMULATOR@ \ dependencies/z-1.0/ebin/z1.@EMULATOR@ +ESCRIPT= \ + escript/someapp-1.0/ebin/mymod.@EMULATOR@ -all: $(OTP9229) $(DEPENDENCIES) + +all: $(OTP9229) $(DEPENDENCIES) $(ESCRIPT) otp_9229/x-1.0/ebin/x.@EMULATOR@: otp_9229/x-1.0/src/x.erl erlc $(EFLAGS) -ootp_9229/x-1.0/ebin otp_9229/x-1.0/src/x.erl @@ -38,3 +41,6 @@ dependencies/y-1.0/ebin/y2.@EMULATOR@: dependencies/y-1.0/src/y2.erl erlc $(EFLAGS) -odependencies/y-1.0/ebin dependencies/y-1.0/src/y2.erl dependencies/z-1.0/ebin/z1.@EMULATOR@: dependencies/z-1.0/src/z1.erl erlc $(EFLAGS) -odependencies/z-1.0/ebin dependencies/z-1.0/src/z1.erl + +escript/someapp-1.0/ebin/mymod.@EMULATOR@: escript/someapp-1.0/src/mymod.erl + erlc $(EFLAGS) -oescript/someapp-1.0/ebin escript/someapp-1.0/src/mymod.erl diff --git a/lib/reltool/test/reltool_server_SUITE_data/escript/someapp-1.0/ebin/someapp.app b/lib/reltool/test/reltool_server_SUITE_data/escript/someapp-1.0/ebin/someapp.app new file mode 100644 index 0000000000..ea2209941e --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/escript/someapp-1.0/ebin/someapp.app @@ -0,0 +1,6 @@ +%% -*- erlang -*- +{application, someapp, + [{description, "Some app for reltool test including archives in escripts"}, + {vsn, "1.0"}, + {modules, [someapp]}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/reltool/test/reltool_server_SUITE_data/escript/someapp-1.0/src/mymod.erl b/lib/reltool/test/reltool_server_SUITE_data/escript/someapp-1.0/src/mymod.erl new file mode 100644 index 0000000000..b6c71c666d --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/escript/someapp-1.0/src/mymod.erl @@ -0,0 +1,26 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(mymod). + +-export([main/1]). + +%%%----------------------------------------------------------------- +%%% escript main function +main(Args) -> + io:format("Root dir: ~s\n", [code:root_dir()]), + io:format("Script args: ~p\n", [Args]). -- cgit v1.2.3 From 6f5e3e16019a3a4f9e9033d185b9c487967ad5fa Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 15 Feb 2012 16:36:27 +0100 Subject: [reltool] Update state and tables consistently for all types of config changes OTP-9794 OTP-9968 The main idea behind the data structure in reltool_server is that the state shall reflect what is explicitly configured, and the tables shall contain this configuration plus everything that is derived. In some cases, however, this was not the complete truth: * the application table was never read * the module table was never updated on undo * the state contained a lot more than what was explicitly configured This commit re-writes major parts of the reltool_server for the sake of unifying the way the state and tables are updated: * The list of applications in the state now only contains those applications and modules for which there are explicit settings in the configuration (given at startup or changed from the GUI) * When changing any bit of the configuration, the tables are always emptied and every part is derived again from the configuration found in the state * All configuration changes now cause a re-read of the file system, meaning that if something has changed in the file system it will be reflected in the result of the configuration change. This is the case even if no file system related configuration is changed (e.g. root dir or lib dirs) (*POSSIBLE INCOMPATIBILITY*) * Requests for applications and modules from the GUI now always read the tables, not the state * When loading a new configuration file via the GUI, the old configuration is completly scratched, and only the new is valid (*POSSIBLE INCOMPATIBILITY*) * The handling of escripts which include archives of applications is changed to always produce one #app record for the escript in addition to one for each inlined application. All modules are listed as parts of the inlined application where it belongs and not as part of the escript's #app record. This is a temporary solution which will be modified and improved. The following bugs are corrected by this commit: * Loading a config which contains an escript via the GUI menu did not produce the same #app record as when loading the same configuration at reltool start. Paths, version and label could differ. * Loading config with same escript (source) twice caused reltool to add same module twice in #app.mods * Loading config with same escript (inlined beam) twice caused reltool to fail saying module is included by two different applications * Loading config which in addition to an existing escript also adds another escript for which the name sorts before the existing one would cause reltool to fail saying "Application name clash" --- lib/reltool/src/reltool_server.erl | 1177 +++++++++++++++-------------- lib/reltool/test/reltool_server_SUITE.erl | 37 +- 2 files changed, 613 insertions(+), 601 deletions(-) (limited to 'lib') diff --git a/lib/reltool/src/reltool_server.erl b/lib/reltool/src/reltool_server.erl index fcd1ad393e..b6ffb9b134 100644 --- a/lib/reltool/src/reltool_server.erl +++ b/lib/reltool/src/reltool_server.erl @@ -138,12 +138,13 @@ do_init(Options) -> %% process_flag(trap_exit, (S#state.common)#common.trap_exit), proc_lib:init_ack(ParentPid, {ok, self(), C, Sys#sys{apps = undefined}}), - {S2, Status2} = refresh(S, true, Status), - {S3, Status3} = - analyse(S2#state{old_sys = S2#state.sys}, Status2), + {S2, Apps, Status2} = refresh(S, true, Status), + Status3 = analyse(S2, Apps, Status2), case Status3 of {ok, _Warnings} -> % BUGBUG: handle warnings - loop(S3#state{status = Status3, old_status = {ok, []}}); + loop(S2#state{old_sys = S2#state.sys, + status = Status3, + old_status = {ok, []}}); {error, Reason} -> exit(Reason) end. @@ -152,40 +153,7 @@ parse_options(Opts) -> AppTab = ets:new(reltool_apps, [public, ordered_set, {keypos, #app.name}]), ModTab = ets:new(reltool_mods, [public, ordered_set, {keypos, #mod.name}]), ModUsesTab = ets:new(reltool_mod_uses, [public, bag, {keypos, 1}]), - Sys = #sys{root_dir = reltool_utils:root_dir(), - lib_dirs = reltool_utils:erl_libs(), - escripts = [], - incl_cond = ?DEFAULT_INCL_COND, - mod_cond = ?DEFAULT_MOD_COND, - apps = ?DEFAULT_APPS, - boot_rel = ?DEFAULT_REL_NAME, - rels = reltool_utils:default_rels(), - emu_name = ?DEFAULT_EMU_NAME, - profile = ?DEFAULT_PROFILE, - incl_sys_filters = dec_re(incl_sys_filters, - ?DEFAULT_INCL_SYS_FILTERS, - []), - excl_sys_filters = dec_re(excl_sys_filters, - ?DEFAULT_EXCL_SYS_FILTERS, - []), - incl_app_filters = dec_re(incl_app_filters, - ?DEFAULT_INCL_APP_FILTERS, - []), - excl_app_filters = dec_re(excl_app_filters, - ?DEFAULT_EXCL_APP_FILTERS, - []), - relocatable = ?DEFAULT_RELOCATABLE, - rel_app_type = ?DEFAULT_REL_APP_TYPE, - embedded_app_type = ?DEFAULT_EMBEDDED_APP_TYPE, - app_file = ?DEFAULT_APP_FILE, - incl_archive_filters = dec_re(incl_archive_filters, - ?DEFAULT_INCL_ARCHIVE_FILTERS, - []), - excl_archive_filters = dec_re(excl_archive_filters, - ?DEFAULT_EXCL_ARCHIVE_FILTERS, - []), - archive_opts = ?DEFAULT_ARCHIVE_OPTS, - debug_info = ?DEFAULT_DEBUG_INFO}, + Sys = default_sys(), C2 = #common{sys_debug = [], wx_debug = 0, trap_exit = true, @@ -195,6 +163,42 @@ parse_options(Opts) -> S = #state{options = Opts}, parse_options(Opts, S, C2, Sys, {ok, []}). +default_sys() -> + #sys{root_dir = reltool_utils:root_dir(), + lib_dirs = reltool_utils:erl_libs(), + escripts = [], + incl_cond = ?DEFAULT_INCL_COND, + mod_cond = ?DEFAULT_MOD_COND, + apps = ?DEFAULT_APPS, + boot_rel = ?DEFAULT_REL_NAME, + rels = reltool_utils:default_rels(), + emu_name = ?DEFAULT_EMU_NAME, + profile = ?DEFAULT_PROFILE, + incl_sys_filters = dec_re(incl_sys_filters, + ?DEFAULT_INCL_SYS_FILTERS, + []), + excl_sys_filters = dec_re(excl_sys_filters, + ?DEFAULT_EXCL_SYS_FILTERS, + []), + incl_app_filters = dec_re(incl_app_filters, + ?DEFAULT_INCL_APP_FILTERS, + []), + excl_app_filters = dec_re(excl_app_filters, + ?DEFAULT_EXCL_APP_FILTERS, + []), + relocatable = ?DEFAULT_RELOCATABLE, + rel_app_type = ?DEFAULT_REL_APP_TYPE, + embedded_app_type = ?DEFAULT_EMBEDDED_APP_TYPE, + app_file = ?DEFAULT_APP_FILE, + incl_archive_filters = dec_re(incl_archive_filters, + ?DEFAULT_INCL_ARCHIVE_FILTERS, + []), + excl_archive_filters = dec_re(excl_archive_filters, + ?DEFAULT_EXCL_ARCHIVE_FILTERS, + []), + archive_opts = ?DEFAULT_ARCHIVE_OPTS, + debug_info = ?DEFAULT_DEBUG_INFO}. + dec_re(Key, Regexps, Old) -> reltool_utils:decode_regexps(Key, Regexps, Old). @@ -252,19 +256,20 @@ loop(#state{common = C, sys = Sys} = S) -> ?MODULE:loop(S); {call, ReplyTo, Ref, reset_config} -> {S2, Status} = parse_options(S#state.options), - S3 = shrink_sys(S2), - {S4, Status2} = refresh(S3, true, Status), - {S5, Status3} = analyse(S4#state{old_sys = S#state.sys}, Status2), - S6 = + {S4, Apps, Status2} = refresh(S2, true, Status), + Status3 = analyse(S4, Apps, Status2), + S5 = case Status3 of {ok, _Warnings} -> - S5#state{status = Status3, old_status = S#state.status}; + S4#state{old_sys = Sys, + status = Status3, + old_status = S#state.status}; {error, _} -> %% Keep old state S end, reltool_utils:reply(ReplyTo, Ref, Status3), - ?MODULE:loop(S6); + ?MODULE:loop(S5); {call, ReplyTo, Ref, undo_config} -> OldSys = S#state.old_sys, S2 = S#state{sys = OldSys, @@ -273,29 +278,29 @@ loop(#state{common = C, sys = Sys} = S) -> %%! If so, consider if it is correct to use Force or not - %%! since warnings from refresh_app will not re-appear here %%! in undo if Force==false. - Force = - (OldSys#sys.root_dir =/= Sys#sys.root_dir) orelse - (OldSys#sys.lib_dirs =/= Sys#sys.lib_dirs) orelse - (OldSys#sys.escripts =/= Sys#sys.escripts), - - {S3, Status} = refresh(S2, Force, S#state.old_status), - {S4, Status2} = analyse(S3, Status), - S5 = + Force = true, +% (OldSys#sys.root_dir =/= Sys#sys.root_dir) orelse +% (OldSys#sys.lib_dirs =/= Sys#sys.lib_dirs) orelse +% (OldSys#sys.escripts =/= Sys#sys.escripts), + + {S3, Apps, Status} = refresh(S2, Force, S#state.old_status), + Status2 = analyse(S3, Apps, Status), + S4 = case Status2 of {ok, _Warnings} -> % BUGBUG: handle warnings - S4#state{status = Status2, old_status = S#state.status}; + S3#state{status = Status2, old_status = S#state.status}; {error, _} -> %% Keep old state S end, reltool_utils:reply(ReplyTo, Ref, Status2), - ?MODULE:loop(S5); + ?MODULE:loop(S4); {call, ReplyTo, Ref, {get_rel, RelName}} -> Sys = S#state.sys, Reply = case lists:keysearch(RelName, #rel.name, Sys#sys.rels) of {value, Rel} -> - reltool_target:gen_rel(Rel, Sys); + reltool_target:gen_rel(Rel, sys_all_apps(C,Sys)); false -> {error, "No such release: " ++ RelName} end, @@ -308,7 +313,8 @@ loop(#state{common = C, sys = Sys} = S) -> {value, Rel} -> PathFlag = true, Vars = [], - reltool_target:gen_script(Rel, Sys, PathFlag, Vars); + reltool_target:gen_script(Rel, sys_all_apps(C,Sys), + PathFlag, Vars); false -> {error, "No such release: " ++ RelName} end, @@ -326,99 +332,110 @@ loop(#state{common = C, sys = Sys} = S) -> ?MODULE:loop(S); {call, ReplyTo, Ref, {get_app, AppName}} when is_atom(AppName) -> Reply = - case lists:keysearch(AppName, #app.name, Sys#sys.apps) of - {value, App} -> + case ets:lookup(C#common.app_tab,AppName) of + [App] -> {ok, App}; - false -> + [] -> {error, "No such application: " ++ atom_to_list(AppName)} end, reltool_utils:reply(ReplyTo, Ref, Reply), ?MODULE:loop(S); {call, ReplyTo, Ref, {set_app, App}} -> - {S2, Status} = do_set_app(S, App, {ok, []}), - {S3, Status2} = analyse(S2#state{old_sys=S#state.sys}, Status), - {S4, Reply} = - case Status2 of + {S2, Status} = do_set_apps(S, [App], {ok, []}), + {S3, Reply} = + case Status of {ok, Warnings} -> - App2 = ?KEYSEARCH(App#app.name, - #app.name, - (S3#state.sys)#sys.apps), - {S3#state{status=Status2, old_status=S#state.status}, + [App2] = ets:lookup(C#common.app_tab,App#app.name), + {S2#state{old_sys=Sys, + status=Status, + old_status=S#state.status}, {ok, App2, Warnings}}; {error, _} -> %% Keep old state - {S, Status2} + {S, Status} end, reltool_utils:reply(ReplyTo, Ref, Reply), - ?MODULE:loop(S4); + ?MODULE:loop(S3); {call, ReplyTo, Ref, {get_apps, Kind}} -> AppNames = case Kind of whitelist -> - [A || - A <- Sys#sys.apps, - A#app.is_pre_included =:= true]; - blacklist -> - [A || - A <- Sys#sys.apps, - A#app.is_pre_included =:= false]; - source -> - [A || - A <- Sys#sys.apps, - A#app.is_included =/= true, - A#app.is_pre_included =/= false]; + %% Pre-included + ets:select(C#common.app_tab, + [{#app{is_pre_included=true,_='_'}, + [], + ['$_']}]); + blacklist -> + %% Pre-excluded + ets:select(C#common.app_tab, + [{#app{is_pre_included=false,_='_'}, + [], + ['$_']}]); + source -> + %% Not included and not pre-excluded + ets:select(C#common.app_tab, + [{#app{is_included='$1', + is_pre_included='$2', + _='_'}, + [{'=/=','$1',true}, + {'=/=','$2',false}], + ['$_']}]); derived -> - [A || - A <- Sys#sys.apps, - A#app.is_included =:= true, - A#app.is_pre_included =/= true] + %% Included, but not pre-included + ets:select(C#common.app_tab, + [{#app{is_included='$1', + is_pre_included='$2', + _='_'}, + [{'=:=','$1',true}, + {'=/=','$2',true}], + ['$_']}]) end, reltool_utils:reply(ReplyTo, Ref, {ok, AppNames}), ?MODULE:loop(S); {call, ReplyTo, Ref, {set_apps, Apps}} -> - {S2, Status} = - lists:foldl(fun(A, {X, Y}) -> do_set_app(X, A, Y) end, - {S, {ok, []}}, - Apps), - {S3, Status2} = analyse(S2#state{old_sys = S#state.sys}, Status), - S4 = - case Status2 of + {S2, Status} = do_set_apps(S, Apps, {ok, []}), + S3 = + case Status of {ok, _Warnings} -> - S3#state{status=Status2, old_status=S#state.status}; + S2#state{old_sys = Sys, + status=Status, + old_status=S#state.status}; {error, _} -> %% Keep old state S end, - reltool_utils:reply(ReplyTo, Ref, Status2), - ?MODULE:loop(S4); + reltool_utils:reply(ReplyTo, Ref, Status), + ?MODULE:loop(S3); {call, ReplyTo, Ref, get_sys} -> reltool_utils:reply(ReplyTo, Ref, {ok, Sys#sys{apps = undefined}}), ?MODULE:loop(S); {call, ReplyTo, Ref, {set_sys, Sys2}} -> S2 = S#state{sys = Sys2#sys{apps = Sys#sys.apps}}, - Force = - (Sys2#sys.root_dir =/= Sys#sys.root_dir) orelse - (Sys2#sys.lib_dirs =/= Sys#sys.lib_dirs) orelse - (Sys2#sys.escripts =/= Sys#sys.escripts), - {S3, Status} = refresh(S2, Force, {ok, []}), - {S4, Status2} = analyse(S3#state{old_sys = S#state.sys}, Status), - S5 = + Force = true, +% (Sys2#sys.root_dir =/= Sys#sys.root_dir) orelse +% (Sys2#sys.lib_dirs =/= Sys#sys.lib_dirs) orelse +% (Sys2#sys.escripts =/= Sys#sys.escripts), + {S3, Apps, Status} = refresh(S2, Force, {ok, []}), + Status2 = analyse(S3, Apps, Status), + S4 = case Status2 of {ok, _Warnings} -> % BUGBUG: handle warnings - S4#state{status = Status2, old_status = S#state.status}; + S3#state{old_sys = Sys, + status = Status2, + old_status = S#state.status}; {error, _} -> %% Keep old state S end, reltool_utils:reply(ReplyTo, Ref, Status2), - ?MODULE:loop(S5); + ?MODULE:loop(S4); {call, ReplyTo, Ref, get_status} -> reltool_utils:reply(ReplyTo, Ref, S#state.status), ?MODULE:loop(S); {call, ReplyTo, Ref, {gen_rel_files, Dir}} -> Status = - case reltool_target:gen_rel_files(S#state.sys, Dir) of + case reltool_target:gen_rel_files(sys_all_apps(C,Sys), Dir) of ok -> {ok, []}; {error, Reason} -> @@ -427,11 +444,11 @@ loop(#state{common = C, sys = Sys} = S) -> reltool_utils:reply(ReplyTo, Ref, Status), ?MODULE:loop(S); {call, ReplyTo, Ref, {gen_target, Dir}} -> - Reply = reltool_target:gen_target(S#state.sys, Dir), + Reply = reltool_target:gen_target(sys_all_apps(C,Sys), Dir), reltool_utils:reply(ReplyTo, Ref, Reply), ?MODULE:loop(S); {call, ReplyTo, Ref, gen_spec} -> - Reply = reltool_target:gen_spec(S#state.sys), + Reply = reltool_target:gen_spec(sys_all_apps(C,Sys)), reltool_utils:reply(ReplyTo, Ref, Reply), ?MODULE:loop(S); {'EXIT', Pid, Reason} when Pid =:= S#state.parent_pid -> @@ -448,67 +465,141 @@ loop(#state{common = C, sys = Sys} = S) -> end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +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)}, + + %% Refresh and analyse + {S2, Apps, Status2} = refresh(S#state{sys = Sys2}, true, Status), + Status3 = analyse(S2, Apps, Status2), + + {S2, Status3}. + +%% 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) -> + NewSysApps = + case app_set_config_only(Config) of + {delete,Name} -> + lists:keydelete(Name,#app.name,SysApps); + New -> + lists:ukeymerge(#app.name,[New],SysApps) + end, + app_update_config(Configs,NewSysApps); +app_update_config([],SysApps) -> + SysApps. + +app_set_config_only(#app{mods=ConfigMods} = Config) -> + app_set_config_only(mod_set_config_only(ConfigMods),Config). + +app_set_config_only([],#app{name = Name, + incl_cond = undefined, + mod_cond = undefined, + use_selected_vsn = undefined, + debug_info = undefined, + app_file = undefined, + app_type = undefined, + incl_app_filters = undefined, + excl_app_filters = undefined, + incl_archive_filters = undefined, + excl_archive_filters = undefined, + archive_opts = undefined}) -> + {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, + incl_app_filters = InclAppFilters, + excl_app_filters = ExclAppFilters, + 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}. + +mod_set_config_only(ConfigMods) -> + [#mod{name = Name, + incl_cond = InclCond, + debug_info = DebugInfo} || + #mod{name = Name, + incl_cond = InclCond, + debug_info = DebugInfo} <- ConfigMods, + (InclCond =/= undefined) orelse (DebugInfo =/= undefined)]. -do_set_app(#state{sys = Sys} = S, App, Status) -> - AppName = App#app.name, - {App2, Status2} = refresh_app(App, false, Status), - Apps = Sys#sys.apps, - Apps2 = lists:keystore(AppName, #app.name, Apps, App2), - Escripts = [A#app.active_dir || A <- Apps2, A#app.is_escript], - Sys2 = Sys#sys{apps = Apps2, escripts = Escripts}, - {S#state{sys = Sys2}, Status2}. - -analyse(#state{common = C, - sys = #sys{apps = Apps0, rels = Rels} = Sys} = S, - Status) -> - Apps = lists:keydelete(?MISSING_APP_NAME, #app.name, Apps0), + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +analyse(#state{common=C, sys=Sys}, Apps, Status) -> ets:delete_all_objects(C#common.app_tab), ets:delete_all_objects(C#common.mod_tab), ets:delete_all_objects(C#common.mod_used_by_tab), - MissingApp = default_app(?MISSING_APP_NAME, "missing"), - ets:insert(C#common.app_tab, MissingApp), - - {RevRelApps, Status2} = apps_in_rels(Rels, Apps, Status), - RelApps2 = lists:reverse(RevRelApps), - {Apps2, Status3} = - lists:mapfoldl(fun(App, Acc) -> - app_init_is_included(C, Sys, App, RelApps2, Acc) - end, - Status2, - Apps), - Apps3 = - case app_propagate_is_included(C, Sys, Apps2, []) of - [] -> - Apps2; - MissingMods -> - %% io:format("Missing mods: ~p\n", [MissingMods]), - MissingApp2 = MissingApp#app{label = ?MISSING_APP_TEXT, - info = missing_app_info(""), - mods = MissingMods, - status = missing, - uses_mods = []}, - [MissingApp2 | Apps2] - end, - app_propagate_is_used_by(C, Apps3), - {Apps4,Status4} = app_recap_dependencies(C, Sys, Apps3, [], Status3), - %% io:format("Missing app: ~p\n", - %% [lists:keysearch(?MISSING_APP_NAME, #app.name, Apps4)]), - Sys2 = Sys#sys{apps = Apps4}, - - case verify_config(RelApps2, Sys2, Status4) of - {ok, _Warnings} = Status5 -> - {S#state{sys = Sys2}, Status5}; - {error, _} = Status5 -> - {S, Status5} - end. + + %% Create a list of {RelName,AppName}, one element for each + %% AppName that needs to be included for the given release. + {RelApps, Status2} = apps_in_rels(Sys#sys.rels, Apps, Status), + + %% Initiate is_pre_included and is_included for all applications + %% based on #sys.incl_cond, #app.incl_cond and if the application + %% is included in a release (rel spec - see apps_in_rels above). + %% Then initiate the same for each module, and check that there + %% are no duplicated module names (in different applications) + %% where we can not decide which one to use. + %% Write all #app to app_tab and all #mod to mod_tab. + Status3 = apps_init_is_included(C, Sys, Apps, RelApps, Status2), + + %% For each module that has #mod.is_included==true, propagate + %% is_included to the modules it uses. + propagate_is_included(C, Sys), + + %% Insert reverse dependencies - i.e. for each + %% #mod{name=Mod, uses_mods=[UsedMod]}, + %% insert an entry {UsedMod,Mod} in mod_used_by_tab. + propagate_is_used_by(C), + + %% Set the above reverse dependencies in #mod records + %% (used_by_mods) and accumulate in #app records. + %% Make sure #app.is_included is always true if some + %% #mod.is_included==true for at least one module in the app. + %% Set status=missing|ok for #app and #mod - indicates if module + %% (.beam file) is missing in file system. + Status4 = app_recap_dependencies(C, Status3), + + %% Check that the boot_rel exists. + %% Check that all applications that are listed in a 'rel' spec are + %% also really included in the target release. + %% Check that all mandatory applications are included in all rels. + verify_config(C, Sys, RelApps, Status4). apps_in_rels(Rels, Apps, Status) -> - lists:foldl(fun(Rel, {RelApps, S}) -> - {MoreRelApps, S2} = apps_in_rel(Rel, Apps, S), - {MoreRelApps ++ RelApps, S2} - end, - {[], Status}, - Rels). + {AllRelApps, Status2} = + lists:foldl(fun(Rel, {RelApps, S}) -> + {MoreRelApps, S2} = apps_in_rel(Rel, Apps, S), + {MoreRelApps ++ RelApps, S2} + end, + {[], Status}, + Rels), + {lists:reverse(AllRelApps), Status2}. apps_in_rel(#rel{name = RelName, rel_apps = RelApps}, Apps, Status) -> Mandatory = [{RelName, kernel}, {RelName, stdlib}], @@ -540,6 +631,14 @@ more_apps_in_rels([{RelName, AppName} = RA | RelApps], Apps, Acc, Status) -> more_apps_in_rels([], _Apps, Acc, Status) -> {Acc, Status}. + +apps_init_is_included(C, Sys, Apps, RelApps, Status) -> + lists:foldl(fun(App, AccStatus) -> + app_init_is_included(C, Sys, App, RelApps, AccStatus) + end, + Status, + Apps). + app_init_is_included(C, Sys, #app{name = AppName, mods = Mods} = A, @@ -587,8 +686,8 @@ app_init_is_included(C, is_pre_included = IsPreIncl, is_included = IsIncl, rels = Rels}, - ets:insert(C#common.app_tab, A2), - {A2, Status3}. + ets:insert(C#common.app_tab, A2), %%! Set mods to only mod names here???? + Status3. mod_init_is_included(C, M, ModCond, AppCond, Default, Status) -> %% print(M#mod.name, hipe, "incl_cond -> ~p\n", [AppCond]), @@ -676,55 +775,42 @@ false_to_undefined(Bool) -> _ -> Bool end. -app_propagate_is_included(C, Sys, [#app{mods = Mods} = A | Apps], Acc) -> - Acc2 = mod_propagate_is_included(C, Sys, A, Mods, Acc), - app_propagate_is_included(C, Sys, Apps, Acc2); -app_propagate_is_included(_C, _Sys, [], Acc) -> - Acc. - -mod_propagate_is_included(C, Sys, A, [#mod{name = ModName} | Mods], Acc) -> - Acc2 = - case ets:lookup(C#common.mod_tab, ModName) of - [M2] when M2#mod.app_name=:=A#app.name -> - %% print(ModName, file, "Maybe Prop ~p -> ~p\n", - %% [M2, M2#mod.is_included]), - %% print(ModName, filename, "Maybe Prop ~p -> ~p\n", - %% [M2, M2#mod.is_included]), - case M2#mod.is_included of - true -> - %% Propagate include mark - mod_mark_is_included(C,Sys,ModName,M2#mod.uses_mods,Acc); - false -> - Acc; - undefined -> - Acc - end; - [_] -> - %% This module is currently used from a different application - %% Ignore - Acc - end, - mod_propagate_is_included(C, Sys, A, Mods, Acc2); -mod_propagate_is_included(_C, _Sys, _A, [], Acc) -> - Acc. +%% Return the list for {ModName, UsesModNames} for all modules where +%% #mod.is_included==true. +get_all_mods_and_dependencies(C) -> + ets:select(C#common.mod_tab, [{#mod{name='$1', + uses_mods='$2', + is_included=true, + _='_'}, + [], + [{{'$1','$2'}}]}]). + +propagate_is_included(C, Sys) -> + case lists:flatmap( + fun({ModName,UsesModNames}) -> + mod_mark_is_included(C,Sys,ModName,UsesModNames,[]) + end, + get_all_mods_and_dependencies(C)) of + [] -> + ok; + MissingMods -> + MissingApp = default_app(?MISSING_APP_NAME, "missing"), + MissingApp2 = MissingApp#app{label = ?MISSING_APP_TEXT, + info = missing_app_info(""), + mods = MissingMods, + status = missing, + uses_mods = []}, + ets:insert(C#common.app_tab, MissingApp2), + ok + end. mod_mark_is_included(C, Sys, UsedByName, [ModName | ModNames], Acc) -> Acc3 = case ets:lookup(C#common.mod_tab, ModName) of [M] -> - %% print(UsedByName, file, "Maybe Mark ~p -> ~p\n", - %% [M, M#mod.is_included]), - %% print(UsedByName, filename, "Maybe Mark ~p -> ~p\n", - %% [M, M#mod.is_included]), case M#mod.is_included of - true -> - %% Already marked - Acc; - false -> - %% Already marked - Acc; undefined -> - %% Mark and propagate + %% Not yet marked => mark and propagate M2 = case M#mod.incl_cond of include -> @@ -737,15 +823,9 @@ mod_mark_is_included(C, Sys, UsedByName, [ModName | ModNames], Acc) -> M#mod{is_included = true} end, ets:insert(C#common.mod_tab, M2), - %% io:format("Propagate mod: ~p -> ~p (~p)\n", - %% [UsedByName, ModName, M#mod.incl_cond]), [A] = ets:lookup(C#common.app_tab, M2#mod.app_name), Acc2 = case A#app.is_included of - true -> - Acc; - false -> - Acc; undefined -> ModCond = case A#app.mod_cond of @@ -763,9 +843,6 @@ mod_mark_is_included(C, Sys, UsedByName, [ModName | ModNames], Acc) -> end end, Mods = lists:filter(Filter, A#app.mods), - %% io:format("Propagate app: ~p ~p -> ~p\n", - %% [UsedByName, A#app.name, - %% [M3#mod.name || M3 <- Mods]]), A2 = A#app{is_included = true}, ets:insert(C#common.app_tab, A2), mod_mark_is_included(C, @@ -773,53 +850,52 @@ mod_mark_is_included(C, Sys, UsedByName, [ModName | ModNames], Acc) -> ModName, [M3#mod.name || M3 <- Mods], - Acc) + Acc); + _ -> + %% Already marked true or false + Acc end, mod_mark_is_included(C, Sys, ModName, M2#mod.uses_mods, - Acc2) + Acc2); + _ -> + %% Already marked true or false + Acc end; [] -> M = missing_mod(ModName, ?MISSING_APP_NAME), M2 = M#mod{is_included = true}, ets:insert(C#common.mod_tab, M2), - ets:insert(C#common.mod_used_by_tab, {UsedByName, ModName}), [M2 | Acc] end, mod_mark_is_included(C, Sys, UsedByName, ModNames, Acc3); mod_mark_is_included(_C, _Sys, _UsedByName, [], Acc) -> Acc. -app_propagate_is_used_by(C, [#app{mods = Mods, name = Name} | Apps]) -> - case Name =:= ?MISSING_APP_NAME of - true -> ok; - false -> ok - end, - mod_propagate_is_used_by(C, Mods), - app_propagate_is_used_by(C, Apps); -app_propagate_is_used_by(_C, []) -> - ok. - -mod_propagate_is_used_by(C, [#mod{name = ModName} | Mods]) -> - [M] = ets:lookup(C#common.mod_tab, ModName), - case M#mod.is_included of - true -> - [ets:insert(C#common.mod_used_by_tab, {UsedModName, ModName}) || - UsedModName <- M#mod.uses_mods]; - false -> - ignore; - undefined -> - ignore - end, - mod_propagate_is_used_by(C, Mods); -mod_propagate_is_used_by(_C, []) -> - ok. - -app_recap_dependencies(C, Sys, [#app{mods = Mods, is_included = IsIncl} = A | Apps], Acc, Status) -> +propagate_is_used_by(C) -> + lists:foreach( + fun({Mod,UsesMods}) -> + lists:foreach( + fun(UsedMod) -> + ets:insert(C#common.mod_used_by_tab,{UsedMod,Mod}) + end, + UsesMods) + end, + get_all_mods_and_dependencies(C)). + + +app_recap_dependencies(C, Status0) -> + ets:foldl(fun(App,Status) -> + app_recap_dependencies(C,App,Status) + end, + Status0, + C#common.app_tab). + +app_recap_dependencies(C, #app{mods = Mods, is_included = IsIncl} = A, Status) -> {Mods2, IsIncl2, Status2} = - mod_recap_dependencies(C, Sys, A, Mods, [], IsIncl, Status), + mod_recap_dependencies(C, A, Mods, [], IsIncl, Status), AppStatus = case lists:keymember(missing, #mod.status, Mods2) of true -> missing; @@ -844,11 +920,9 @@ app_recap_dependencies(C, Sys, [#app{mods = Mods, is_included = IsIncl} = A | Ap used_by_apps = UsedByApps2, is_included = IsIncl2}, ets:insert(C#common.app_tab,A2), - app_recap_dependencies(C, Sys, Apps, [A2 | Acc], Status2); -app_recap_dependencies(_C, _Sys, [], Acc, Status) -> - {lists:reverse(Acc), Status}. + Status2. -mod_recap_dependencies(C, Sys, A, [#mod{name = ModName}=M1 | Mods], Acc, IsIncl, Status) -> +mod_recap_dependencies(C, A, [#mod{name = ModName}=M1 | Mods], Acc, IsIncl, Status) -> case ets:lookup(C#common.mod_tab, ModName) of [M2] when M2#mod.app_name=:=A#app.name -> ModStatus = do_get_status(M2), @@ -864,12 +938,12 @@ mod_recap_dependencies(C, Sys, A, [#mod{name = ModName}=M1 | Mods], Acc, IsIncl, {IsIncl, M2#mod{status = ModStatus, used_by_mods = []}} end, ets:insert(C#common.mod_tab, M3), - mod_recap_dependencies(C, Sys, A, Mods, [M3 | Acc], IsIncl2, Status); - [_] when A#app.is_included==false; M1#mod.incl_cond==exclude -> + mod_recap_dependencies(C, A, Mods, [M3 | Acc], IsIncl2, Status); + [_] when A#app.is_included==false; M1#mod.incl_cond==exclude -> %!!! incl_cond could be read from #sys.app.mods %% App is explicitely excluded so it is ok that the module %% record does not exist for this module in this %% application. - mod_recap_dependencies(C, Sys, A, Mods, [M1 | Acc], IsIncl, Status); + mod_recap_dependencies(C, A, Mods, [M1 | Acc], IsIncl, Status); [M2] -> %% A module is potensially included by multiple %% applications. This is not allowed! @@ -879,9 +953,9 @@ mod_recap_dependencies(C, Sys, A, [#mod{name = ModName}=M1 | Mods], Acc, IsIncl, " potentially included by two different applications: ", A#app.name, " and ", M2#mod.app_name, "."]), Status2 = reltool_utils:return_first_error(Status,Error), - mod_recap_dependencies(C, Sys, A, Mods, [M1 | Acc], IsIncl, Status2) + mod_recap_dependencies(C, A, Mods, [M1 | Acc], IsIncl, Status2) end; -mod_recap_dependencies(_C, _Sys, _A, [], Acc, IsIncl, Status) -> +mod_recap_dependencies(_C, _A, [], Acc, IsIncl, Status) -> {lists:reverse(Acc), IsIncl, Status}. do_get_status(M) -> @@ -892,48 +966,53 @@ do_get_status(M) -> ok end. -shrink_sys(#state{sys = #sys{apps = Apps} = Sys} = S) -> - Apps2 = lists:zf(fun filter_app/1, Apps), - S#state{sys = Sys#sys{apps = Apps2}}. - -filter_app(A) -> - Mods = [M#mod{is_app_mod = undefined, - is_ebin_mod = undefined, - uses_mods = undefined, - exists = false} || - M <- A#app.mods, - M#mod.incl_cond =/= undefined], - if - A#app.is_escript -> - {true, A#app{vsn = undefined, - label = undefined, - info = undefined, - mods = [], - uses_mods = undefined}}; - Mods =:= [], - A#app.mod_cond =:= undefined, - A#app.incl_cond =:= undefined, - A#app.use_selected_vsn =:= undefined -> - false; +verify_config(C, #sys{boot_rel = BootRel, rels = Rels}, RelApps, Status) -> + case lists:keymember(BootRel, #rel.name, Rels) of true -> - {Dir, Dirs, OptVsn} = - case A#app.use_selected_vsn of - undefined -> - {shrinked, [], undefined}; - false -> - {shrinked, [], undefined}; - true -> - {A#app.active_dir, [A#app.active_dir], A#app.vsn} - end, - {true, A#app{active_dir = Dir, - sorted_dirs = Dirs, - vsn = OptVsn, - label = undefined, - info = undefined, - mods = Mods, - uses_mods = undefined}} + Status2 = lists:foldl(fun(RA, Acc) -> + check_app(C, RA, Acc) end, + Status, + RelApps), + lists:foldl(fun(#rel{name = RelName}, Acc)-> + check_rel(RelName, RelApps, Acc) + end, + Status2, + Rels); + false -> + Text = lists:concat(["Release ", BootRel, + " is mandatory (used as boot_rel)"]), + reltool_utils:return_first_error(Status, Text) end. +check_app(C, {RelName, AppName}, Status) -> + case ets:lookup(C#common.app_tab, AppName) of + [#app{is_pre_included=IsPreIncl, is_included=IsIncl}] + when IsPreIncl; IsIncl -> + Status; + _ -> + Text = lists:concat(["Release ", RelName, + " uses non included application ", + AppName]), + reltool_utils:return_first_error(Status, Text) + end. + +check_rel(RelName, RelApps, Status) -> + EnsureApp = + fun(AppName, Acc) -> + case lists:member({RelName, AppName}, RelApps) of + true -> + Acc; + false -> + Text = lists:concat(["Mandatory application ", + AppName, + " is not included in release ", + RelName]), + reltool_utils:return_first_error(Acc, Text) + end + end, + Mandatory = [kernel, stdlib], + lists:foldl(EnsureApp, Status, Mandatory). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% refresh_app(#app{name = AppName, @@ -962,12 +1041,17 @@ refresh_app(#app{name = AppName, AppName, DefaultVsn, Status), + + %% 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, - %% Add non-existing modules + %% Add non-existing modules - i.e. create default #mod + %% records for all modules that are listed in .app file + %% but do not exist in ebin. AppInfoMods = AppInfo#app_info.modules, AppModNames = case AppInfo#app_info.mod of @@ -981,11 +1065,19 @@ refresh_app(#app{name = AppName, end, MissingMods = add_missing_mods(AppName, EbinMods, AppModNames), - %% Add optional user config for each module + %% Add optional user config for each module. + %% The #mod records that are already in the #app record at + %% this point do only contain user defined configuration + %% (set by parse_options/1). So here we merge with the + %% default records from above. Mods2 = add_mod_config(MissingMods ++ EbinMods, Mods), - %% Set app flag for each module in app file + %% Set app flag for each module in app file, i.e. the flag + %% which indicates if the module is listed in the .app + %% file or not. The start module also get the flag set to true. Mods3 = set_mod_flags(Mods2, AppModNames), + + %% Finally, set label and update the #app record AppVsn = AppInfo#app_info.vsn, AppLabel = case AppVsn of @@ -1196,12 +1288,54 @@ set_mod_flags(Mods, AppModNames) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% do_get_config(S, InclDef, InclDeriv) -> - S2 = + AppTab = (S#state.common)#common.app_tab, + Sys = case InclDeriv of - false -> shrink_sys(S); - true -> S + false -> + %% Only the apps that exist in #sys.apps shall be + %% included,and they shall be minimized + Apps = [shrink_app(App) || + #app{name=Name} <- (S#state.sys)#sys.apps, + App <- ets:lookup(AppTab,Name)], + (S#state.sys)#sys{apps=Apps}; + true -> + sys_all_apps(S#state.common,S#state.sys) end, - reltool_target:gen_config(S2#state.sys, InclDef). + reltool_target:gen_config(Sys, InclDef). + +shrink_app(A) -> + Mods = [M#mod{is_app_mod = undefined, + is_ebin_mod = undefined, + uses_mods = undefined, + exists = false} || + M <- A#app.mods, + M#mod.incl_cond =/= undefined], + if + A#app.is_escript -> + A#app{vsn = undefined, + label = undefined, + info = undefined, + mods = [], + uses_mods = undefined}; + true -> + {Dir, Dirs, OptVsn} = + case A#app.use_selected_vsn of + undefined -> + {shrinked, [], undefined}; + false -> + {shrinked, [], undefined}; + true -> + {A#app.active_dir, [A#app.active_dir], A#app.vsn} + end, + A#app{active_dir = Dir, + sorted_dirs = Dirs, + vsn = OptVsn, + label = undefined, + info = undefined, + mods = Mods, + uses_mods = undefined} + end. + do_save_config(S, Filename, InclDef, InclDeriv) -> {ok, Config} = do_get_config(S, InclDef, InclDeriv), @@ -1212,26 +1346,22 @@ do_save_config(S, Filename, InclDef, InclDeriv) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% do_load_config(S, SysConfig) -> - OldSys = S#state.sys, - S2 = shrink_sys(S), - ShrinkedSys = S2#state.sys, - {NewSys, Status} = - read_config(ShrinkedSys#sys{apps = []}, SysConfig, {ok, []}), + {NewSys, Status} = read_config(default_sys(), SysConfig, {ok, []}), case Status of {ok, _Warnings} -> - Force = false, - {MergedSys, Status2} = merge_config(OldSys, NewSys, Force, Status), - {S3, Status3} = - analyse(S2#state{sys = MergedSys, old_sys = OldSys}, Status2), - S4 = + {S2, Apps, Status2} = refresh(S#state{sys=NewSys}, true, Status), + Status3 = analyse(S2, Apps, Status2), + S3 = case Status3 of {ok, _Warnings2} -> - S3#state{status = Status3, old_status = S#state.status}; + S2#state{old_sys = S#state.sys, + status = Status3, + old_status = S#state.status}; {error, _} -> %% Keep old state S end, - {S4, Status3}; + {S3, Status3}; {error, _} -> %% Keep old state {S, Status} @@ -1298,17 +1428,12 @@ decode(#sys{apps = Apps} = Sys, [{app, Name, AppKeyVals} | SysKeyVals], Status) {App2, Status2} = decode(App, AppKeyVals, Status), decode(Sys#sys{apps = [App2 | Apps]}, SysKeyVals, Status2); decode(#sys{apps = Apps, escripts = Escripts} = Sys, - [{escript, File, AppKeyVals} | SysKeyVals], Status) - when is_list(File), is_list(AppKeyVals) -> - {Name, Label} = split_escript_name(File), - App = default_app(Name, File), - App2 = App#app{is_escript = true, - label = Label, - info = missing_app_info(""), - active_dir = File, - sorted_dirs = [File]}, - {App3, Status2} = decode(App2, AppKeyVals, Status), - decode(Sys#sys{apps = [App3 | Apps], escripts = [File | Escripts]}, + [{escript, File0, AppKeyVals} | SysKeyVals], Status) + when is_list(File0), is_list(AppKeyVals) -> + File = filename:absname(File0), + App = default_escript_app(File), + {App2, Status2} = decode(App, AppKeyVals, Status), + decode(Sys#sys{apps = [App2 | Apps], escripts = [File | Escripts]}, SysKeyVals, Status2); decode(#sys{rels = Rels} = Sys, [{rel, Name, Vsn, RelApps} | SysKeyVals], @@ -1554,79 +1679,50 @@ split_escript_name(File) when is_list(File) -> Label = filename:basename(File, ".escript"), {list_to_atom("*escript* " ++ Label), Label}. +default_escript_app(File) -> + {Name, Label} = split_escript_name(File), + App = default_app(Name, File), + App#app{is_escript = true, + label = Label, + info = missing_app_info("")}. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -refresh(#state{sys = Sys} = S, Force, Status) -> - {Sys2, Status2} = merge_config(Sys, Sys#sys{apps = []}, Force, Status), - {S#state{sys = Sys2}, Status2}. - -merge_config(OldSys, NewSys, Force, Status) -> - RootDir = filename:absname(NewSys#sys.root_dir), - LibDirs = [filename:absname(D) || D <- NewSys#sys.lib_dirs], - Escripts = [filename:absname(E) || E <- NewSys#sys.escripts], - {SourceDirs, Status2} = - libs_to_dirs(RootDir, LibDirs, Status), - MergedApps = merge_app_dirs(SourceDirs, NewSys#sys.apps, OldSys#sys.apps), - {AllApps, Status3} = - escripts_to_apps(Escripts, MergedApps, OldSys#sys.apps, Status2), - {RefreshedApps, Status4} = - refresh_apps(OldSys#sys.apps, AllApps, [], Force, Status3), - {PatchedApps, Status5} = - patch_erts_version(RootDir, RefreshedApps, Status4), - Escripts2 = [A#app.active_dir || A <- PatchedApps, A#app.is_escript], - NewSys2 = NewSys#sys{root_dir = RootDir, - lib_dirs = LibDirs, - escripts = Escripts2, - apps = PatchedApps}, - {NewSys2, Status5}. +%% Apps is a list of #app records - sorted on #app.name - containing +%% only the apps that have specific configuration (e.g. in the config +%% file) +refresh(#state{sys=Sys} = S, Force, Status) -> + RootDir = filename:absname(Sys#sys.root_dir), + LibDirs = [filename:absname(D) || D <- Sys#sys.lib_dirs], + Escripts = [filename:absname(E) || E <- Sys#sys.escripts], -verify_config(RelApps, #sys{boot_rel = BootRel, rels = Rels, apps = Apps}, Status) -> - case lists:keymember(BootRel, #rel.name, Rels) of - true -> - Status2 = lists:foldl(fun(RA, Acc) -> - check_app(RA, Apps, Acc) end, - Status, - RelApps), - lists:foldl(fun(#rel{name = RelName}, Acc)-> - check_rel(RelName, RelApps, Acc) - end, - Status2, - Rels); - false -> - Text = lists:concat(["Release ", BootRel, - " is mandatory (used as boot_rel)"]), - reltool_utils:return_first_error(Status, Text) - end. + %% Read all lib dirs and return sorted [{AppName,Dir}] + {SourceDirs, Status2} = libs_to_dirs(RootDir, LibDirs, Status), -check_app({RelName, AppName}, Apps, Status) -> - case lists:keysearch(AppName, #app.name, Apps) of - {value, App} when App#app.is_pre_included -> - Status; - {value, App} when App#app.is_included -> - Status; - _ -> - Text = lists:concat(["Release ", RelName, - " uses non included application ", - AppName]), - reltool_utils:return_first_error(Status, Text) - end. + %% Create #app records for all apps in SourceDirs, and merge with + %% list of apps from config. + MergedApps = merge_app_dirs(SourceDirs, Sys#sys.apps), -check_rel(RelName, RelApps, Status) -> - EnsureApp = - fun(AppName, Acc) -> - case lists:member({RelName, AppName}, RelApps) of - true -> - Acc; - false -> - Text = lists:concat(["Mandatory application ", - AppName, - " is not included in release ", - RelName]), - reltool_utils:return_first_error(Acc, Text) - end - end, - Mandatory = [kernel, stdlib], - lists:foldl(EnsureApp, Status, Mandatory). + %% For each escript, find all related files and convert to #app + %% and #mod records + {AllApps, Status3} = escripts_to_apps(Escripts, MergedApps, Status2), + + %% Make sure correct version of each application is used according + %% to the user configuration. + %% Then find all modules and their dependencies and set user + %% configuration per module if it exists. + {RefreshedApps, Status4} = refresh_apps(Sys#sys.apps, AllApps, [], + Force, Status3), + + %% Make sure erts exists in app list and has a version (or warn) + {PatchedApps, Status5} = patch_erts_version(RootDir, RefreshedApps, Status4), + + %% Update #sys and return + Escripts2 = [A#app.active_dir || A <- PatchedApps, A#app.is_escript], + Sys2 = Sys#sys{root_dir = RootDir, + lib_dirs = LibDirs, + escripts = Escripts2}, + {S#state{sys=Sys2}, PatchedApps, Status5}. patch_erts_version(RootDir, Apps, Status) -> AppName = erts, @@ -1717,7 +1813,7 @@ app_dirs2([Lib | Libs], Acc, Status) -> app_dirs2([], Acc, Status) -> {lists:sort(lists:append(Acc)), Status}. -escripts_to_apps([Escript | Escripts], Apps, OldApps, Status) -> +escripts_to_apps([Escript | Escripts], Apps, Status) -> {EscriptAppName, _Label} = split_escript_name(Escript), Ext = code:objfile_extension(), Fun = fun(FullName, _GetInfo, GetBin, {FileAcc, StatusAcc}) -> @@ -1780,92 +1876,67 @@ escripts_to_apps([Escript | Escripts], Apps, OldApps, Status) -> end, case reltool_utils:escript_foldl(Fun, {[], Status}, Escript) of {ok, {Files, Status2}} -> + EscriptApp = + case lists:keyfind(EscriptAppName,#app.name,Apps) of + false -> default_escript_app(Escript); + EA -> EA + end, {Apps2, Status3} = - files_to_apps(Escript, - lists:sort(Files), - Apps, - Apps, - OldApps, - Status2), - escripts_to_apps(Escripts, Apps2, OldApps, Status3); + escript_files_to_apps(Escript, + lists:sort(Files), + [EscriptApp], + Apps, + Status2), + escripts_to_apps(Escripts, Apps2, Status3); {error, Reason} -> Text = lists:flatten(io_lib:format("~p", [Reason])), {[], reltool_utils:return_first_error(Status, "Illegal escript " ++ Escript ++ ": " ++ Text)} end; -escripts_to_apps([], Apps, _OldApps, Status) -> +escripts_to_apps([], Apps, Status) -> {Apps, Status}. %% Assume that all files for an app are in consecutive order %% Assume the app info is before the mods -files_to_apps(Escript, - [{AppName, Type, Dir, ModOrInfo} | Files] = AllFiles, - Acc, - Apps, - OldApps, - Status) -> - case Type of - mod -> - case Acc of - [] -> - Info = missing_app_info(""), - {NewApp, Status2} = - merge_escript_app(AppName, - Dir, - Info, - [ModOrInfo], - Apps, - OldApps, - Status), - files_to_apps(Escript, - AllFiles, - [NewApp | Acc], - Apps, - OldApps, Status2); - [App | Acc2] when App#app.name =:= ModOrInfo#mod.app_name -> - App2 = App#app{mods = [ModOrInfo | App#app.mods]}, - files_to_apps(Escript, - Files, - [App2 | Acc2], - Apps, - OldApps, - Status); - [App | Acc2] -> - PrevApp = App#app{mods = lists:keysort(#mod.name, - App#app.mods)}, - Info = missing_app_info(""), - {NewApp, Status2} = - merge_escript_app(AppName, - Dir, - Info, - [ModOrInfo], - Apps, - OldApps, - Status), - files_to_apps(Escript, - Files, - [NewApp, PrevApp | Acc2], - Apps, - OldApps, - Status2) - end; - app -> - {App, Status2} = - merge_escript_app(AppName, Dir, ModOrInfo, [], Apps, OldApps, - Status), - files_to_apps(Escript, Files, [App | Acc], Apps, OldApps, Status2) - end; -files_to_apps(_Escript, [], Acc, _Apps, _OldApps, Status) -> - {lists:keysort(#app.name, Acc), Status}. - -merge_escript_app(AppName, Dir, Info, Mods, Apps, OldApps, Status) -> - App1 = case lists:keyfind(AppName, #app.name, OldApps) of - #app{} = App -> - App; - false -> - default_app(AppName, Dir) - end, +escript_files_to_apps(Escript, + [{AppName, Type, Dir, ModOrInfo} | Files], + Acc, + Apps, + Status) -> + {NewAcc,Status3} = + case Type of + mod -> + case Acc of + [App | Acc2] when App#app.name =:= ModOrInfo#mod.app_name -> + Mods = lists:ukeymerge(#mod.name, + [ModOrInfo], + App#app.mods), + {[App#app{mods = Mods} | Acc2], Status}; + Acc -> + {NewApp, Status2} = init_escript_app(AppName, + Dir, + missing_app_info(""), + [ModOrInfo], + Apps, + Status), + {[NewApp | Acc], Status2} + end; + app -> + {App, Status2} = init_escript_app(AppName, + Dir, + ModOrInfo, + [], + Apps, + 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}. + +init_escript_app(AppName, Dir, Info, Mods, Apps, Status) -> + App1 = default_app(AppName, Dir), App2 = App1#app{is_escript = true, label = filename:basename(Dir, ".escript"), info = Info, @@ -1882,54 +1953,30 @@ merge_escript_app(AppName, Dir, Info, Mods, Apps, OldApps, Status) -> {App2, Status} end. -merge_app_dirs([{Name, Dir} | Rest], [App | Apps], OldApps) - when App#app.name =:= Name -> - %% Add new dir to app - App2 = App#app{sorted_dirs = [Dir | App#app.sorted_dirs]}, - merge_app_dirs(Rest, [App2 | Apps], OldApps); -merge_app_dirs([{Name, Dir} | Rest], Apps, OldApps) -> - %% Initate app - Apps2 = sort_app_dirs(Apps), - Apps4 = +merge_app_dirs([{Name, Dir} | Rest], Apps) -> + App = case lists:keyfind(Name, #app.name, Apps) of false -> - case lists:keyfind(Name, #app.name, OldApps) of - false -> - App = default_app(Name, Dir), - [App | Apps2]; - #app{active_dir = Dir} = OldApp -> - [OldApp | Apps2]; - OldApp -> - App = - case filter_app(OldApp) of - {true, NewApp} -> - NewApp#app{active_dir = Dir, - sorted_dirs = [Dir]}; - false -> - default_app(Name, Dir) - end, - [App | Apps2] - end; + default_app(Name, Dir); OldApp -> - Apps3 = lists:keydelete(Name, #app.name, Apps2), - App = OldApp#app{sorted_dirs = [Dir | OldApp#app.sorted_dirs]}, - [App | Apps3] + SortedDirs = lists:umerge(fun reltool_utils:app_dir_test/2, + [Dir], OldApp#app.sorted_dirs), + OldApp#app{sorted_dirs = SortedDirs} end, - merge_app_dirs(Rest, Apps4, OldApps); -merge_app_dirs([], Apps, _OldApps) -> - Apps2 = sort_app_dirs(Apps), - lists:reverse(Apps2). - -sort_app_dirs([#app{sorted_dirs = Dirs} = App | Acc]) -> - SortedDirs = lists:sort(fun reltool_utils:app_dir_test/2, Dirs), - case SortedDirs of - [ActiveDir | _] -> ok; - [] -> ActiveDir = undefined - end, - [App#app{active_dir = ActiveDir, sorted_dirs = SortedDirs} | Acc]; -sort_app_dirs([]) -> + Apps2 = lists:ukeymerge(#app.name, [App], Apps), + merge_app_dirs(Rest, Apps2); +merge_app_dirs([], Apps) -> + set_active_dirs(Apps). + +%% First dir, i.e. the one with highest version, is set to active dir +set_active_dirs([#app{sorted_dirs = [ActiveDir|_]} = App | Apps]) -> + [App#app{active_dir = ActiveDir} | set_active_dirs(Apps)]; +set_active_dirs([#app{sorted_dirs = []} = App | Apps]) -> + [App#app{active_dir = undefined} | set_active_dirs(Apps)]; +set_active_dirs([]) -> []. + default_app(Name, Dir) -> App = default_app(Name), App#app{active_dir = Dir, @@ -1938,82 +1985,44 @@ default_app(Name, Dir) -> default_app(Name) -> #app{name = Name, is_escript = false, - use_selected_vsn = undefined, - active_dir = undefined, sorted_dirs = [], - vsn = undefined, - label = undefined, - info = undefined, mods = [], - - mod_cond = undefined, - incl_cond = undefined, - - status = missing, - uses_mods = undefined, - is_pre_included = undefined, - is_included = undefined, - rels = undefined}. - -%% Assume that the application are sorted -refresh_apps([Old | OldApps], [New | NewApps], Acc, Force, Status) - when New#app.name =:= Old#app.name -> - {Info, ActiveDir, Status2} = ensure_app_info(New, Status), - OptLabel = - case Info#app_info.vsn =:= New#app.vsn of - true -> New#app.label; - false -> undefined % Cause refresh - end, - {Refreshed, Status3} = - refresh_app(New#app{label = OptLabel, - active_dir = ActiveDir, - vsn = Info#app_info.vsn, - info = Info}, - Force, - Status2), - refresh_apps(OldApps, NewApps, [Refreshed | Acc], Force, Status3); -refresh_apps([Old | OldApps], [New | NewApps], Acc, Force, Status) - when New#app.name < Old#app.name -> - %% No old app version exists. Use new as is. - %% BUGBUG: Issue warning if the active_dir is not defined - {New2, Status2} = refresh_app(New, Force, Status), - refresh_apps([Old | OldApps], NewApps, [New2 | Acc], Force, Status2); -refresh_apps([Old | OldApps], [New | NewApps], Acc, Force, Status) - when New#app.name > Old#app.name -> - %% No new version. Remove the old. - Status2 = - case Old#app.name =:= ?MISSING_APP_NAME of - true -> - Status; - false -> - Warning = - lists:concat([Old#app.name, - ": The source dirs does not ", - "contain the application anymore."]), - reltool_utils:add_warning(Status, Warning) - end, - refresh_apps(OldApps, [New | NewApps], Acc, Force, Status2); -refresh_apps([], [New | NewApps], Acc, Force, Status) -> - %% No old app version exists. Use new as is. - {New2, Status2} = refresh_app(New, Force, Status), - refresh_apps([], NewApps, [New2 | Acc], Force, Status2); -refresh_apps([Old | OldApps], [], Acc, Force, Status) -> - %% No new version. Remove the old. - Status2 = - case Old#app.name =:= ?MISSING_APP_NAME of - true -> - Status; - false -> - Warning = - lists:concat([Old#app.name, - ": The source dirs does not " - "contain the application anymore."]), - reltool_utils:add_warning(Status, Warning) - end, - refresh_apps(OldApps, [], Acc, Force, Status2); -refresh_apps([], [], Acc, _Force, Status) -> + status = missing}. + + + +refresh_apps(ConfigApps, [New | NewApps], Acc, Force, Status) -> + {New2, Status3} = + case lists:keymember(New#app.name,#app.name,ConfigApps) of + true -> + %% There is user defined config for this application, make + %% sure that the application exists and that correct + %% version is used. Set active directory. + {Info, ActiveDir, Status2} = ensure_app_info(New, Status), + OptLabel = + case Info#app_info.vsn =:= New#app.vsn of + true -> New#app.label; + false -> undefined % Cause refresh + end, + refresh_app(New#app{label = OptLabel, + active_dir = ActiveDir, + vsn = Info#app_info.vsn, + info = Info}, + Force, + Status2); + false -> + %% There is no user defined config for this + %% application. This means that the app is found in the + %% lib dirs, and that the highest version shall be + %% used. I.e. the active_dir and vsn are already correct + %% from merge_app_dirs. + refresh_app(New, Force, Status) + end, + refresh_apps(ConfigApps, NewApps, [New2 | Acc], Force, Status3); +refresh_apps(_ConfigApps, [], Acc, _Force, Status) -> {lists:reverse(Acc), Status}. + ensure_app_info(#app{is_escript = true, active_dir = Dir, info = Info}, Status) -> {Info, Dir, Status}; @@ -2092,6 +2101,12 @@ get_base(Name, Dir) -> filename:basename(Dir) end. +sys_all_apps(C,Sys) -> + Sys#sys{apps = all_apps(C)}. + +all_apps(C) -> + ets:match_object(C#common.app_tab,'_'). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% sys callbacks diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl index 56c9cb5ed1..f2b63f78f0 100644 --- a/lib/reltool/test/reltool_server_SUITE.erl +++ b/lib/reltool/test/reltool_server_SUITE.erl @@ -1192,8 +1192,10 @@ set_app_and_undo(Config) -> ExclCover = Cover#mod{incl_cond=exclude}, Mods = Tools#app.mods, Tools1 = Tools#app{mods = lists:keyreplace(cover,#mod.name,Mods,ExclCover)}, - {ok,ToolsNoCover,[]} = ?msym({ok,_,[]}, reltool_server:set_app(Pid,Tools1)), - ?m({ok,[]}, reltool_server:get_status(Pid)), + {ok,ToolsNoCover,["a: Cannot parse app file"++_|_]} = + ?msym({ok,_,["a: Cannot parse app file"++_|_]}, + reltool_server:set_app(Pid,Tools1)), + ?msym({ok,["a: Cannot parse app file"++_|_]}, reltool_server:get_status(Pid)), %% Check that the module is no longer included ?m({ok,ToolsNoCover}, reltool_server:get_app(Pid,tools)), @@ -1201,17 +1203,16 @@ set_app_and_undo(Config) -> reltool_server:get_mod(Pid,cover)), %% Undo - %%! warning can come twice here... :( ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:undo_config(Pid)), ?m({ok,Tools}, reltool_server:get_app(Pid,tools)), ?m({ok,Cover}, reltool_server:get_mod(Pid,cover)), ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:get_status(Pid)), %% Undo again, to check that it toggles - ?m({ok,[]}, reltool_server:undo_config(Pid)), + ?msym({ok,["a: Cannot parse app file"++_|_]}, reltool_server:undo_config(Pid)), ?m({ok,ToolsNoCover}, reltool_server:get_app(Pid,tools)), ?m({ok,NoIncludeCover}, reltool_server:get_mod(Pid,cover)), - ?m({ok,[]}, reltool_server:get_status(Pid)), + ?msym({ok,["a: Cannot parse app file"++_|_]}, reltool_server:get_status(Pid)), ?m(ok, reltool:stop(Pid)), ok. @@ -1238,8 +1239,9 @@ set_apps_and_undo(Config) -> %% Exclude one application with set_apps ExclTools = Tools#app{incl_cond=exclude}, - ?m({ok,[]}, reltool_server:set_apps(Pid,[ExclTools])), - ?m({ok,[]}, reltool_server:get_status(Pid)), + ?msym({ok,["a: Cannot parse app file"++_|_]}, + reltool_server:set_apps(Pid,[ExclTools])), + ?msym({ok,["a: Cannot parse app file"++_|_]}, reltool_server:get_status(Pid)), %% Check that the app and its modules (one of them) are no longer included {ok,NoTools} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), @@ -1249,17 +1251,16 @@ set_apps_and_undo(Config) -> reltool_server:get_mod(Pid,cover)), %% Undo - %%! warning can come twice here... :( ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:undo_config(Pid)), ?m({ok,Tools}, reltool_server:get_app(Pid,tools)), ?m({ok,Cover}, reltool_server:get_mod(Pid,cover)), ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:get_status(Pid)), %% Undo again, to check that it toggles - ?m({ok,[]}, reltool_server:undo_config(Pid)), + ?msym({ok,["a: Cannot parse app file"++_|_]}, reltool_server:undo_config(Pid)), ?m({ok,NoTools}, reltool_server:get_app(Pid,tools)), ?m({ok,NoIncludeCover}, reltool_server:get_mod(Pid,cover)), - ?m({ok,[]}, reltool_server:get_status(Pid)), + ?msym({ok,["a: Cannot parse app file"++_|_]}, reltool_server:get_status(Pid)), ?m(ok, reltool:stop(Pid)), ok. @@ -1327,7 +1328,7 @@ load_config_and_undo(Config) -> {app,sasl,[{incl_cond,include}]}, {app,stdlib,[{incl_cond,include}]}, {app,tools,[{incl_cond,derived}]}]}, - ?msym({ok,["a: Cannot parse app file"++_]}, + ?msym({ok,["a: Cannot parse app file"++_|_]}, reltool_server:load_config(Pid,Sys2)), %%% OTP-0702, 15) ?m({ok, Sys2}, reltool:get_config(Pid)), %%% Note that {incl_cond,exclude} is removed compared to Sys1 - @@ -1363,7 +1364,6 @@ load_config_and_undo(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Load config with escript -load_config_escript_path(_Config) -> {skip,"Known bug: loading config with escript at reltool start creates different #app record than loading same config with load_config"}; load_config_escript_path(Config) -> %% Create escript DataDir = ?config(data_dir,Config), @@ -1411,7 +1411,6 @@ load_config_escript_path(Config) -> %% Load config with same (source) escript twice and check that the %% application information is not changed. -load_config_same_escript_source(_Config) -> {skip,"Known bug: loading config with same escript (source) twice causes reltool to add same module twice in #app.mods"}; load_config_same_escript_source(_Config) -> %% Create escript ExDir = code:lib_dir(reltool, examples), @@ -1428,12 +1427,14 @@ load_config_same_escript_source(_Config) -> ]}, {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), - {ok,[#app{name='*escript* display_args'}=A]} = - ?msym({ok,[_]}, reltool_server:get_apps(Pid,whitelist)), +% {ok,[#app{name='*escript* display_args'}]} = + ?msym({ok,[#app{name='*escript* display_args',mods=[_]}]}, + reltool_server:get_apps(Pid,whitelist)), %% Load the same config again, then check that app is not changed ?m({ok,[]}, reltool_server:load_config(Pid,Sys)), - ?m({ok,[A]}, reltool_server:get_apps(Pid,whitelist)), + ?msym({ok,[#app{name='*escript* display_args',mods=[_]}]}, + reltool_server:get_apps(Pid,whitelist)), ?m(ok, reltool:stop(Pid)), @@ -1443,7 +1444,6 @@ load_config_same_escript_source(_Config) -> %% Load config with same (beam) escript twice and check that the %% application information is not changed. -load_config_same_escript_beam(_Config) -> {skip,"Known bug: loading config with same escript (with inlined beam) twice causes reltool to fail and say module is included by two different applications"}; load_config_same_escript_beam(Config) -> %% Create escript DataDir = ?config(data_dir,Config), @@ -1478,8 +1478,6 @@ load_config_same_escript_beam(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Load config with escript -%% BUG: see OTP-9792, 25) -load_config_add_escript(_Config) -> {skip,"Known bug: Can not load_config which in addition to an existing escript also adds another escript for which the name sorts before the existing one"}; load_config_add_escript(Config) -> %% First escript ExDir = code:lib_dir(reltool, examples), @@ -1566,7 +1564,6 @@ reset_config_and_undo(Config) -> reltool_server:get_mod(Pid,cover)), %% Reset - %%! warning can come twice here... :( ?msym({ok,["a: Cannot parse app file"++_|_]}, reltool_server:reset_config(Pid)), ?m({ok,Tools1}, reltool_server:get_app(Pid,tools)), -- cgit v1.2.3 From 0bc47607cad0c9ad475a7c0a8e7aa5633d00ceb5 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 16 Feb 2012 14:21:19 +0100 Subject: [reltool] Fix badmatch when starting GUI OTP-9792 Start of reltool GUI sometimes crashes with a badmatch in reltool_sys_win:do_init/1 because the #sys record fetched with reltool_server:get_sys/1 differs from the #sys record returned from reltool_server:start_link/1. This has been corrected. reltool_server:start_link/1 no longer retuns the #sys record. --- lib/reltool/src/reltool.erl | 4 ++-- lib/reltool/src/reltool_server.erl | 5 ++--- lib/reltool/src/reltool_sys_win.erl | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/reltool/src/reltool.erl b/lib/reltool/src/reltool.erl index 54eb1ca9e1..a2b1c9468c 100644 --- a/lib/reltool/src/reltool.erl +++ b/lib/reltool/src/reltool.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 @@ -66,7 +66,7 @@ start_link(Options) when is_list(Options) -> -spec start_server(options()) -> {ok, server_pid()} | {error, reason()}. start_server(Options) -> case reltool_server:start_link(Options) of - {ok, ServerPid, _Common, _Sys} -> + {ok, ServerPid, _Common} -> {ok, ServerPid}; {error, Reason} -> {error, lists:flatten(io_lib:format("~p", [Reason]))} diff --git a/lib/reltool/src/reltool_server.erl b/lib/reltool/src/reltool_server.erl index b6ffb9b134..da15d91581 100644 --- a/lib/reltool/src/reltool_server.erl +++ b/lib/reltool/src/reltool_server.erl @@ -133,11 +133,10 @@ init(Options) -> do_init(Options) -> {S, Status} = parse_options(Options), - #state{parent_pid = ParentPid, common = C, sys = Sys} = S, + #state{parent_pid = ParentPid, common = C} = S, %% process_flag(trap_exit, (S#state.common)#common.trap_exit), - proc_lib:init_ack(ParentPid, - {ok, self(), C, Sys#sys{apps = undefined}}), + proc_lib:init_ack(ParentPid, {ok, self(), C}), {S2, Apps, Status2} = refresh(S, true, Status), Status3 = analyse(S2, Apps, Status2), case Status3 of diff --git a/lib/reltool/src/reltool_sys_win.erl b/lib/reltool/src/reltool_sys_win.erl index f29a95af38..c49641d6a3 100644 --- a/lib/reltool/src/reltool_sys_win.erl +++ b/lib/reltool/src/reltool_sys_win.erl @@ -135,7 +135,7 @@ init(Options) -> do_init([{safe_config, Safe}, {parent, Parent} | Options]) -> case reltool_server:start_link(Options) of - {ok, ServerPid, C, Sys} -> + {ok, ServerPid, C} -> process_flag(trap_exit, C#common.trap_exit), wx:new(), wx:debug(C#common.wx_debug), -- cgit v1.2.3 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(-) (limited to 'lib') 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 From c714eff72c1bd15f8831cffe7256f3dfd8faf806 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 23 Feb 2012 11:26:27 +0100 Subject: [reltool] Fix rollback and undo OTP-9794 Backup old configuration before starting re-configuration so rollback is possible if configuration fails. Store last configuration including derivates so undo does no longer need to refresh and analyse everything from disk. --- lib/reltool/src/reltool.hrl | 4 +- lib/reltool/src/reltool_server.erl | 266 ++++++++++++++---------------- lib/reltool/src/reltool_sys_win.erl | 23 +-- lib/reltool/test/reltool_server_SUITE.erl | 60 +++++-- 4 files changed, 181 insertions(+), 172 deletions(-) (limited to 'lib') diff --git a/lib/reltool/src/reltool.hrl b/lib/reltool/src/reltool.hrl index 0a90c42ce2..781c3f6d2c 100644 --- a/lib/reltool/src/reltool.hrl +++ b/lib/reltool/src/reltool.hrl @@ -130,7 +130,9 @@ trap_exit :: boolean(), app_tab :: ets:tab(), mod_tab :: ets:tab(), - mod_used_by_tab :: ets:tab() + mod_used_by_tab :: ets:tab(), + old_app_tab :: ets:tab(), + old_mod_tab :: ets:tab() }). -record(mod, diff --git a/lib/reltool/src/reltool_server.erl b/lib/reltool/src/reltool_server.erl index f5abaf0957..e9b26b26da 100644 --- a/lib/reltool/src/reltool_server.erl +++ b/lib/reltool/src/reltool_server.erl @@ -132,33 +132,32 @@ init(Options) -> end. do_init(Options) -> - {S, Status} = parse_options(Options), - #state{parent_pid = ParentPid, common = C} = S, - - %% process_flag(trap_exit, (S#state.common)#common.trap_exit), - proc_lib:init_ack(ParentPid, {ok, self(), C}), - {S2, Apps, Status2} = refresh(S, true, Status), - Status3 = analyse(S2, Apps, Status2), - case Status3 of - {ok, _Warnings} -> % BUGBUG: handle warnings - loop(S2#state{old_sys = S2#state.sys, - status = Status3, - old_status = {ok, []}}); - {error, Reason} -> - exit(Reason) - end. - -parse_options(Opts) -> AppTab = ets:new(reltool_apps, [public, ordered_set, {keypos, #app.name}]), ModTab = ets:new(reltool_mods, [public, ordered_set, {keypos, #mod.name}]), + OldAppTab = ets:new(reltool_apps, [public, ordered_set, {keypos, #app.name}]), + OldModTab = ets:new(reltool_mods, [public, ordered_set, {keypos, #mod.name}]), ModUsesTab = ets:new(reltool_mod_uses, [public, bag, {keypos, 1}]), + InitialC = #common{app_tab = AppTab, + mod_tab = ModTab, + old_app_tab = OldAppTab, + old_mod_tab = OldModTab, + mod_used_by_tab = ModUsesTab}, + + {S, Status} = parse_options(InitialC, Options), + %%! Check status before returning ok? + + proc_lib:init_ack(S#state.parent_pid, {ok, self(), S#state.common}), + + %% This will do exit if it fails + {S2, _Status2} = refresh_and_analyse_no_rollback(S, Status), + %%! what to do about warnings? + loop(S2). + +parse_options(C, Opts) -> Sys = default_sys(), - C2 = #common{sys_debug = [], - wx_debug = 0, - trap_exit = true, - app_tab = AppTab, - mod_tab = ModTab, - mod_used_by_tab = ModUsesTab}, + C2 = C#common{sys_debug = [], + wx_debug = 0, + trap_exit = true}, S = #state{options = Opts}, parse_options(Opts, S, C2, Sys, {ok, []}). @@ -246,54 +245,31 @@ loop(#state{common = C, sys = Sys} = S) -> reltool_utils:reply(ReplyTo, Ref, Reply), ?MODULE:loop(S); {call, ReplyTo, Ref, {load_config, SysConfig}} -> - {S2, Reply} = do_load_config(S, SysConfig), - reltool_utils:reply(ReplyTo, Ref, Reply), - ?MODULE:loop(S2); + {S2, Status} = do_load_config(S, SysConfig), + {S3, Status2} = refresh_and_analyse(S, S2, Status), + reltool_utils:reply(ReplyTo, Ref, Status2), + ?MODULE:loop(S3); {call, ReplyTo, Ref, {save_config, Filename, InclDef, InclDeriv}} -> Reply = do_save_config(S, Filename, InclDef, InclDeriv), reltool_utils:reply(ReplyTo, Ref, Reply), ?MODULE:loop(S); {call, ReplyTo, Ref, reset_config} -> - {S2, Status} = parse_options(S#state.options), - {S4, Apps, Status2} = refresh(S2, true, Status), - Status3 = analyse(S4, Apps, Status2), - S5 = - case Status3 of - {ok, _Warnings} -> - S4#state{old_sys = Sys, - status = Status3, - old_status = S#state.status}; - {error, _} -> - %% Keep old state - S - end, - reltool_utils:reply(ReplyTo, Ref, Status3), - ?MODULE:loop(S5); - {call, ReplyTo, Ref, undo_config} -> - OldSys = S#state.old_sys, - S2 = S#state{sys = OldSys, - old_sys = Sys}, - %%! Possibly skip old_status here, since we do all job again! - %%! If so, consider if it is correct to use Force or not - - %%! since warnings from refresh_app will not re-appear here - %%! in undo if Force==false. - Force = true, -% (OldSys#sys.root_dir =/= Sys#sys.root_dir) orelse -% (OldSys#sys.lib_dirs =/= Sys#sys.lib_dirs) orelse -% (OldSys#sys.escripts =/= Sys#sys.escripts), - - {S3, Apps, Status} = refresh(S2, Force, S#state.old_status), - Status2 = analyse(S3, Apps, Status), - S4 = - case Status2 of - {ok, _Warnings} -> % BUGBUG: handle warnings - S3#state{status = Status2, old_status = S#state.status}; - {error, _} -> - %% Keep old state - S - end, + {S2, Status} = parse_options(C, S#state.options), + {S3, Status2} = refresh_and_analyse(S, S2, Status), reltool_utils:reply(ReplyTo, Ref, Status2), - ?MODULE:loop(S4); + ?MODULE:loop(S3); + {call, ReplyTo, Ref, undo_config} -> + C2 = C#common{app_tab = C#common.old_app_tab, + old_app_tab = C#common.app_tab, + mod_tab = C#common.old_mod_tab, + old_mod_tab = C#common.mod_tab}, + S2 = S#state{common = C2, + sys = S#state.old_sys, + old_sys = Sys, + status = S#state.old_status, + old_status = S#state.status}, + reltool_utils:reply(ReplyTo, Ref, ok), + ?MODULE:loop(S2); {call, ReplyTo, Ref, {get_rel, RelName}} -> Sys = S#state.sys, Reply = @@ -341,18 +317,15 @@ loop(#state{common = C, sys = Sys} = S) -> reltool_utils:reply(ReplyTo, Ref, Reply), ?MODULE:loop(S); {call, ReplyTo, Ref, {set_app, App}} -> - {S2, Status} = do_set_apps(S, [App], {ok, []}), - {S3, Reply} = - case Status of + {S2, Status} = do_set_apps(S, [App]), + {S3, Status2} = refresh_and_analyse(S, S2, Status), + Reply = + case Status2 of {ok, Warnings} -> [App2] = ets:lookup(C#common.app_tab,App#app.name), - {S2#state{old_sys=Sys, - status=Status, - old_status=S#state.status}, - {ok, App2, Warnings}}; + {ok, App2, Warnings}; {error, _} -> - %% Keep old state - {S, Status} + Status2 end, reltool_utils:reply(ReplyTo, Ref, Reply), ?MODULE:loop(S3); @@ -393,42 +366,18 @@ loop(#state{common = C, sys = Sys} = S) -> reltool_utils:reply(ReplyTo, Ref, {ok, AppNames}), ?MODULE:loop(S); {call, ReplyTo, Ref, {set_apps, Apps}} -> - {S2, Status} = do_set_apps(S, Apps, {ok, []}), - S3 = - case Status of - {ok, _Warnings} -> - S2#state{old_sys = Sys, - status=Status, - old_status=S#state.status}; - {error, _} -> - %% Keep old state - S - end, - reltool_utils:reply(ReplyTo, Ref, Status), + {S2, Status} = do_set_apps(S, Apps), + {S3, Status2} = refresh_and_analyse(S, S2, Status), + reltool_utils:reply(ReplyTo, Ref, Status2), ?MODULE:loop(S3); {call, ReplyTo, Ref, get_sys} -> reltool_utils:reply(ReplyTo, Ref, {ok, Sys#sys{apps = undefined}}), ?MODULE:loop(S); {call, ReplyTo, Ref, {set_sys, Sys2}} -> - S2 = S#state{sys = Sys2#sys{apps = Sys#sys.apps}}, - Force = true, -% (Sys2#sys.root_dir =/= Sys#sys.root_dir) orelse -% (Sys2#sys.lib_dirs =/= Sys#sys.lib_dirs) orelse -% (Sys2#sys.escripts =/= Sys#sys.escripts), - {S3, Apps, Status} = refresh(S2, Force, {ok, []}), - Status2 = analyse(S3, Apps, Status), - S4 = - case Status2 of - {ok, _Warnings} -> % BUGBUG: handle warnings - S3#state{old_sys = Sys, - status = Status2, - old_status = S#state.status}; - {error, _} -> - %% Keep old state - S - end, - reltool_utils:reply(ReplyTo, Ref, Status2), - ?MODULE:loop(S4); + S2 = S#state{sys = Sys2#sys{apps = Sys#sys.apps}}, + {S3, Status} = refresh_and_analyse(S, S2, {ok,[]}), + reltool_utils:reply(ReplyTo, Ref, Status), + ?MODULE:loop(S3); {call, ReplyTo, Ref, get_status} -> reltool_utils:reply(ReplyTo, Ref, S#state.status), ?MODULE:loop(S); @@ -463,18 +412,12 @@ loop(#state{common = C, sys = Sys} = S) -> ?MODULE:loop(S) end. - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -do_set_apps(#state{sys = Sys} = S, ChangedApps, Status) -> + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +do_set_apps(#state{sys = Sys} = S, ChangedApps) -> %% Create new list of configured applications - {SysApps,Status2} = app_update_config(ChangedApps, Sys#sys.apps, Status), - Sys2 = Sys#sys{apps = SysApps}, - - %% Refresh from filesystem and analyse dependencies - {S2, Apps, Status3} = refresh(S#state{sys = Sys2}, true, Status2), - Status4 = analyse(S2, Apps, Status3), - - {S2, Status4}. + {SysApps,Status} = app_update_config(ChangedApps, Sys#sys.apps, {ok,[]}), + {S#state{sys = Sys#sys{apps = SysApps}}, Status}. %% Re-create the #sys.apps list by %% 1) taking configurable fields from the changed #app records and @@ -577,10 +520,6 @@ mod_set_config_only(ConfigMods) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% analyse(#state{common=C, sys=Sys}, Apps, Status) -> - ets:delete_all_objects(C#common.app_tab), - ets:delete_all_objects(C#common.mod_tab), - ets:delete_all_objects(C#common.mod_used_by_tab), - %% Create a list of {RelName,AppName}, one element for each %% AppName that needs to be included for the given release. {RelApps, Status2} = apps_in_rels(Sys#sys.rels, Apps, Status), @@ -1373,25 +1312,7 @@ do_save_config(S, Filename, InclDef, InclDeriv) -> do_load_config(S, SysConfig) -> {NewSys, Status} = read_config(default_sys(), SysConfig, {ok, []}), - case Status of - {ok, _Warnings} -> - {S2, Apps, Status2} = refresh(S#state{sys=NewSys}, true, Status), - Status3 = analyse(S2, Apps, Status2), - S3 = - case Status3 of - {ok, _Warnings2} -> - S2#state{old_sys = S#state.sys, - status = Status3, - old_status = S#state.status}; - {error, _} -> - %% Keep old state - S - end, - {S3, Status3}; - {error, _} -> - %% Keep old state - {S, Status} - end. + {S#state{sys = NewSys}, Status}. read_config(OldSys, Filename, Status) when is_list(Filename) -> case file:consult(Filename) of @@ -1717,7 +1638,7 @@ default_escript_app(File) -> %% Apps is a list of #app records - sorted on #app.name - containing %% only the apps that have specific configuration (e.g. in the config %% file) -refresh(#state{sys=Sys} = S, Force, Status) -> +refresh(#state{sys=Sys} = S, Status) -> RootDir = filename:absname(Sys#sys.root_dir), LibDirs = [filename:absname(D) || D <- Sys#sys.lib_dirs], Escripts = [filename:absname(E) || E <- Sys#sys.escripts], @@ -1738,7 +1659,7 @@ refresh(#state{sys=Sys} = S, Force, Status) -> %% Then find all modules and their dependencies and set user %% configuration per module if it exists. {RefreshedApps, Status4} = refresh_apps(Sys#sys.apps, AllApps, [], - Force, Status3), + true, Status3), %% Make sure erts exists in app list and has a version (or warn) {PatchedApps, Status5} = patch_erts_version(RootDir, RefreshedApps, Status4), @@ -2143,6 +2064,71 @@ sys_all_apps(C,Sys) -> all_apps(C) -> ets:match_object(C#common.app_tab,'_'). +refresh_and_analyse_no_rollback(#state{common=C} = S, {ok,_} = Status) -> + case refresh(S, Status) of + {S2, Apps, {ok, _}=Status2} -> + case analyse(S2, Apps, Status2) of + {ok, _} = Status3 -> + %% Set old_xxx is equal to xxx + FakeBackup = {ets:tab2list(C#common.app_tab), + ets:tab2list(C#common.mod_tab)}, + save_old(S2, S2, FakeBackup, Status3); + {error,Reason} -> + exit(Reason) + end; + {_,_,{error,Reason}} -> + exit(Reason) + end; +refresh_and_analyse_no_rollback(_,{error,Reason}) -> + exit(Reason). + +refresh_and_analyse(OldS, S, {ok,_}=Status) -> + case refresh(S, Status) of + {S2, Apps, {ok,_}=Status2} -> + %% Analyse will write to app_tab and mod_tab, so we first + %% backup these tables and clear them + Backup = backup(OldS), + case analyse(S2, Apps, Status2) of + {ok, _} = Status3 -> + save_old(OldS, S2, Backup, Status3); + Status3 -> + restore(Backup,OldS), + {OldS,Status3} + end; + {_, _, Status2} -> + {OldS, Status2} + end; +refresh_and_analyse(OldS, _S, Status) -> + {OldS,Status}. + + +backup(#state{common=C}) -> + Apps = ets:tab2list(C#common.app_tab), + Mods = ets:tab2list(C#common.mod_tab), + ets:delete_all_objects(C#common.app_tab), + ets:delete_all_objects(C#common.mod_tab), + ets:delete_all_objects(C#common.mod_used_by_tab), %tmp tab, no backup needed + {Apps,Mods}. + +restore({Apps,Mods}, #state{common=C}) -> + insert_all(C#common.app_tab,Apps), + insert_all(C#common.mod_tab,Mods). + +save_old(#state{status=OldStatus,sys=OldSys},#state{common=C}=NewS, + {OldApps,OldMods},NewStatus) -> + ets:delete_all_objects(C#common.old_app_tab), + ets:delete_all_objects(C#common.old_mod_tab), + insert_all(C#common.old_app_tab,OldApps), + insert_all(C#common.old_mod_tab,OldMods), + {NewS#state{old_sys=OldSys, + old_status=OldStatus, + status=NewStatus}, + NewStatus}. + +insert_all(Tab,Items) -> + lists:foreach(fun(Item) -> ets:insert(Tab,Item) end, Items). + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% sys callbacks diff --git a/lib/reltool/src/reltool_sys_win.erl b/lib/reltool/src/reltool_sys_win.erl index c49641d6a3..1638acb145 100644 --- a/lib/reltool/src/reltool_sys_win.erl +++ b/lib/reltool/src/reltool_sys_win.erl @@ -1290,15 +1290,7 @@ reset_config(#state{status_bar = Bar} = S) -> undo_config(#state{status_bar = Bar} = S) -> wxStatusBar:setStatusText(Bar, "Processing libraries..."), - case reltool_server:undo_config(S#state.server_pid) of - {ok, []} -> - ok; - {ok, Warnings} -> - Msg = lists:flatten([[W, $\n] || W <- Warnings]), - display_message(Msg, ?wxICON_WARNING); - {error, Reason} -> - display_message(Reason, ?wxICON_ERROR) - end, + ok = reltool_server:undo_config(S#state.server_pid), refresh(S). load_config(#state{status_bar = Bar, config_file = OldFile} = S) -> @@ -1452,17 +1444,8 @@ undo_dialog(S, Warnings) -> ?wxID_OK -> true; ?wxID_CANCEL -> - case reltool_server:undo_config(S#state.server_pid) of - {ok, _} -> - false; - {error, Reason} -> - Msg = "FATAL - undo failed:\n\n" ++ - Reason ++ - "\n\nTerminating...", - display_message(Msg, ?wxICON_ERROR), - io:format("~p(~p): ~s\n", [?MODULE, ?LINE, Reason]), - exit(Reason) - end + ok = reltool_server:undo_config(S#state.server_pid), + false end. display_message(Message, Icon) -> diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl index e279be82a8..2588c0829f 100644 --- a/lib/reltool/test/reltool_server_SUITE.erl +++ b/lib/reltool/test/reltool_server_SUITE.erl @@ -79,6 +79,7 @@ all() -> set_apps_inlined, set_sys_and_undo, load_config_and_undo, + load_config_fail, load_config_escript_path, load_config_same_escript_source, load_config_same_escript_beam, @@ -1235,13 +1236,13 @@ set_app_and_undo(Config) -> reltool_server:get_mod(Pid,cover)), %% Undo - ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:undo_config(Pid)), + ?m(ok, reltool_server:undo_config(Pid)), ?m({ok,Tools}, reltool_server:get_app(Pid,tools)), ?m({ok,Cover}, reltool_server:get_mod(Pid,cover)), - ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:get_status(Pid)), + ?msym({ok,["a: Cannot parse app file"++_|_]}, reltool_server:get_status(Pid)), %% Undo again, to check that it toggles - ?msym({ok,["a: Cannot parse app file"++_|_]}, reltool_server:undo_config(Pid)), + ?msym(ok, reltool_server:undo_config(Pid)), ?m({ok,ToolsNoCover}, reltool_server:get_app(Pid,tools)), ?m({ok,NoIncludeCover}, reltool_server:get_mod(Pid,cover)), ?msym({ok,["a: Cannot parse app file"++_|_]}, reltool_server:get_status(Pid)), @@ -1283,13 +1284,13 @@ set_apps_and_undo(Config) -> reltool_server:get_mod(Pid,cover)), %% Undo - ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:undo_config(Pid)), + ?m(ok, reltool_server:undo_config(Pid)), ?m({ok,Tools}, reltool_server:get_app(Pid,tools)), ?m({ok,Cover}, reltool_server:get_mod(Pid,cover)), ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:get_status(Pid)), %% Undo again, to check that it toggles - ?msym({ok,["a: Cannot parse app file"++_|_]}, reltool_server:undo_config(Pid)), + ?m(ok, reltool_server:undo_config(Pid)), ?m({ok,NoTools}, reltool_server:get_app(Pid,tools)), ?m({ok,NoIncludeCover}, reltool_server:get_mod(Pid,cover)), ?msym({ok,["a: Cannot parse app file"++_|_]}, reltool_server:get_status(Pid)), @@ -1391,12 +1392,12 @@ set_sys_and_undo(Config) -> ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:get_status(Pid)), %% Undo - ?m({ok,[]}, reltool_server:undo_config(Pid)), + ?m(ok, reltool_server:undo_config(Pid)), ?m({ok,SysRec}, reltool_server:get_sys(Pid)), ?m({ok,[]}, reltool_server:get_status(Pid)), %% Undo again, to check that it toggles - ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:undo_config(Pid)), + ?m(ok,reltool_server:undo_config(Pid)), ?m({ok,NewSysRec}, reltool_server:get_sys(Pid)), ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:get_status(Pid)), @@ -1448,13 +1449,13 @@ load_config_and_undo(Config) -> reltool_server:get_mod(Pid,cover)), %% Undo - ?m({ok,[]}, reltool_server:undo_config(Pid)), + ?m(ok, reltool_server:undo_config(Pid)), ?m({ok,Tools1}, reltool_server:get_app(Pid,tools)), ?m({ok,Cover1}, reltool_server:get_mod(Pid,cover)), ?m({ok,[]}, reltool_server:get_status(Pid)), %% Undo again, to check that it toggles - ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:undo_config(Pid)), + ?m(ok, reltool_server:undo_config(Pid)), ?m({ok,Tools2}, reltool_server:get_app(Pid,tools)), ?m({ok,Cover2}, reltool_server:get_mod(Pid,cover)), ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:get_status(Pid)), @@ -1463,6 +1464,43 @@ load_config_and_undo(Config) -> ok. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Test that load_config is properly rolled back if it fails +load_config_fail(_Config) -> + Sys1 = {sys,[{incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,tools,[{incl_cond,include}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys1}])), + ?m({ok, Sys1}, reltool:get_config(Pid)), + ?m({ok,[]}, reltool_server:get_status(Pid)), + + %% Get app and mod + {ok,Tools} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), + + %% Try to load a config with a faulty rel statement (includes a + %% non-existing application) + Sys2 = {sys,[{incl_cond, exclude}, + {boot_rel, "faulty_rel"}, + {rel, "faulty_rel", "1.0", [kernel, sasl, stdlib, xxx]}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}]}, + ?msym({error,"Release faulty_rel uses non existing application xxx"}, + reltool_server:load_config(Pid,Sys2)), + + %% Check that a rollback is done to the old configuration + ?m({ok, Sys1}, reltool:get_config(Pid,false,false)), + + %% and that tools is not changed (i.e. that the new configuration + %% is not applied) + ?m({ok,Tools}, reltool_server:get_app(Pid,tools)), + + ?m(ok, reltool:stop(Pid)), + ok. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Load config with escript @@ -1673,13 +1711,13 @@ reset_config_and_undo(Config) -> ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:get_status(Pid)), %% Undo - ?m({ok,[]}, reltool_server:undo_config(Pid)), + ?m(ok, reltool_server:undo_config(Pid)), ?m({ok,Tools2}, reltool_server:get_app(Pid,tools)), ?m({ok,Cover2}, reltool_server:get_mod(Pid,cover)), ?m({ok,[]}, reltool_server:get_status(Pid)), %% Undo again, to check that it toggles - ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:undo_config(Pid)), + ?m(ok, reltool_server:undo_config(Pid)), ?m({ok,Tools1}, reltool_server:get_app(Pid,tools)), ?m({ok,Cover1}, reltool_server:get_mod(Pid,cover)), ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:get_status(Pid)), -- cgit v1.2.3 From 537773c0e6969cb89f92aec0244ba29b07ec66ea Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 24 Feb 2012 15:52:34 +0100 Subject: [reltool] Improve error handling OTP-9794 Stopping configuration (throw/catch) at first error instead of continuing through all steps and then returning the error at the end. Start of reltool_server will no longer succeed if the configuration causes an error. reltool:get_status can only return {error,Reason} in the case where the reltool_server has terminated. In all other cases it will return {ok,Warnings}. Bug fix in this commit: * warnings are no longer duplicated in pop-up or return from reltool_server --- lib/reltool/src/reltool.erl | 6 +- lib/reltool/src/reltool_server.erl | 673 ++++++++++++------------------ lib/reltool/src/reltool_sys_win.erl | 73 ++-- lib/reltool/src/reltool_utils.erl | 35 +- lib/reltool/test/reltool_server_SUITE.erl | 44 +- 5 files changed, 334 insertions(+), 497 deletions(-) (limited to 'lib') diff --git a/lib/reltool/src/reltool.erl b/lib/reltool/src/reltool.erl index a2b1c9468c..2bdf222aa0 100644 --- a/lib/reltool/src/reltool.erl +++ b/lib/reltool/src/reltool.erl @@ -59,17 +59,17 @@ start_link(Options) when is_list(Options) -> {ok, _WinPid} = OK -> OK; {error, Reason} -> - {error, lists:flatten(io_lib:format("~p", [Reason]))} + {error, Reason} end. %% Start server process with options -spec start_server(options()) -> {ok, server_pid()} | {error, reason()}. start_server(Options) -> case reltool_server:start_link(Options) of - {ok, ServerPid, _Common} -> + {ok, ServerPid, _Common, _Sys} -> {ok, ServerPid}; {error, Reason} -> - {error, lists:flatten(io_lib:format("~p", [Reason]))} + {error, Reason} end. %% Start server process with options diff --git a/lib/reltool/src/reltool_server.erl b/lib/reltool/src/reltool_server.erl index e9b26b26da..4c22898559 100644 --- a/lib/reltool/src/reltool_server.erl +++ b/lib/reltool/src/reltool_server.erl @@ -123,10 +123,12 @@ gen_spec(Pid) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Server -init(Options) -> +init([{parent,Parent}|_] = Options) -> try do_init(Options) catch + throw:{error,Reason} -> + proc_lib:init_ack(Parent,{error,Reason}); error:Reason -> exit({Reason, erlang:get_stacktrace()}) end. @@ -143,15 +145,16 @@ do_init(Options) -> old_mod_tab = OldModTab, mod_used_by_tab = ModUsesTab}, - {S, Status} = parse_options(InitialC, Options), - %%! Check status before returning ok? - - proc_lib:init_ack(S#state.parent_pid, {ok, self(), S#state.common}), - - %% This will do exit if it fails - {S2, _Status2} = refresh_and_analyse_no_rollback(S, Status), - %%! what to do about warnings? - loop(S2). + S = parse_options(InitialC, Options), + {S2, Apps, Status2} = refresh(S), + Status3 = analyse(S2, Apps, Status2), + %% Set old_xxx equal to xxx to allow undo=nop + FakeBackup = {ets:tab2list((S2#state.common)#common.app_tab), + ets:tab2list((S2#state.common)#common.mod_tab)}, + S3 = save_old(S2, S2, FakeBackup, Status3), + #state{parent_pid = Parent, sys=Sys, common=C} = S3, + proc_lib:init_ack(Parent, {ok, self(), C, Sys#sys{apps=undefined}}), + loop(S3). parse_options(C, Opts) -> Sys = default_sys(), @@ -159,7 +162,7 @@ parse_options(C, Opts) -> wx_debug = 0, trap_exit = true}, S = #state{options = Opts}, - parse_options(Opts, S, C2, Sys, {ok, []}). + parse_options(Opts, S, C2, Sys). default_sys() -> #sys{root_dir = reltool_utils:root_dir(), @@ -200,36 +203,29 @@ default_sys() -> dec_re(Key, Regexps, Old) -> reltool_utils:decode_regexps(Key, Regexps, Old). -parse_options([{Key, Val} | KeyVals], S, C, Sys, Status) -> +parse_options([{Key, Val} | KeyVals], S, C, Sys) -> case Key of parent -> - parse_options(KeyVals, S#state{parent_pid = Val}, C, Sys, Status); + parse_options(KeyVals, S#state{parent_pid = Val}, C, Sys); sys_debug -> - parse_options(KeyVals, S, C#common{sys_debug = Val}, Sys, Status); + parse_options(KeyVals, S, C#common{sys_debug = Val}, Sys); wx_debug -> - parse_options(KeyVals, S, C#common{wx_debug = Val}, Sys, Status); + parse_options(KeyVals, S, C#common{wx_debug = Val}, Sys); trap_exit -> - parse_options(KeyVals, S, C#common{trap_exit = Val}, Sys, Status); + parse_options(KeyVals, S, C#common{trap_exit = Val}, Sys); config -> - {Sys2, Status2} = read_config(Sys, Val, Status), - parse_options(KeyVals, S, C, Sys2, Status2); + Sys2 = read_config(Sys, Val), + parse_options(KeyVals, S, C, Sys2); sys -> - {Sys2, Status2} = read_config(Sys, {sys, Val}, Status), - parse_options(KeyVals, S, C, Sys2, Status2); + Sys2 = read_config(Sys, {sys, Val}), + parse_options(KeyVals, S, C, Sys2); _ -> - Text = lists:flatten(io_lib:format("~p", [{Key, Val}])), - Status2 = - reltool_utils:return_first_error(Status, - "Illegal option: " ++ Text), - parse_options(KeyVals, S, C, Sys, Status2) + reltool_utils:throw_error("Illegal option: ~p", [{Key, Val}]) end; -parse_options([], S, C, Sys, Status) -> - {S#state{common = C, sys = Sys}, Status}; -parse_options(KeyVals, S, C, Sys, Status) -> - Text = lists:flatten(io_lib:format("~p", [KeyVals])), - Status2 = reltool_utils:return_first_error(Status, - "Illegal options: " ++ Text), - {S#state{common = C, sys = Sys}, Status2}. +parse_options([], S, C, Sys) -> + S#state{common = C, sys = Sys}; +parse_options(KeyVals, _S, _C, _Sys) -> + reltool_utils:throw_error("Illegal option: ~p", [KeyVals]). loop(#state{common = C, sys = Sys} = S) -> receive @@ -245,8 +241,8 @@ loop(#state{common = C, sys = Sys} = S) -> reltool_utils:reply(ReplyTo, Ref, Reply), ?MODULE:loop(S); {call, ReplyTo, Ref, {load_config, SysConfig}} -> - {S2, Status} = do_load_config(S, SysConfig), - {S3, Status2} = refresh_and_analyse(S, S2, Status), + Fun = fun() -> do_load_config(S, SysConfig) end, + {S3, Status2} = config_and_refresh(S, Fun), reltool_utils:reply(ReplyTo, Ref, Status2), ?MODULE:loop(S3); {call, ReplyTo, Ref, {save_config, Filename, InclDef, InclDeriv}} -> @@ -254,8 +250,8 @@ loop(#state{common = C, sys = Sys} = S) -> reltool_utils:reply(ReplyTo, Ref, Reply), ?MODULE:loop(S); {call, ReplyTo, Ref, reset_config} -> - {S2, Status} = parse_options(C, S#state.options), - {S3, Status2} = refresh_and_analyse(S, S2, Status), + Fun = fun() -> parse_options(C, S#state.options) end, + {S3, Status2} = config_and_refresh(S, Fun), reltool_utils:reply(ReplyTo, Ref, Status2), ?MODULE:loop(S3); {call, ReplyTo, Ref, undo_config} -> @@ -317,8 +313,8 @@ loop(#state{common = C, sys = Sys} = S) -> reltool_utils:reply(ReplyTo, Ref, Reply), ?MODULE:loop(S); {call, ReplyTo, Ref, {set_app, App}} -> - {S2, Status} = do_set_apps(S, [App]), - {S3, Status2} = refresh_and_analyse(S, S2, Status), + Fun = fun() -> do_set_apps(S, [App]) end, + {S3, Status2} = config_and_refresh(S, Fun), Reply = case Status2 of {ok, Warnings} -> @@ -366,16 +362,16 @@ loop(#state{common = C, sys = Sys} = S) -> reltool_utils:reply(ReplyTo, Ref, {ok, AppNames}), ?MODULE:loop(S); {call, ReplyTo, Ref, {set_apps, Apps}} -> - {S2, Status} = do_set_apps(S, Apps), - {S3, Status2} = refresh_and_analyse(S, S2, Status), + Fun = fun() -> do_set_apps(S, Apps) end, + {S3, Status2} = config_and_refresh(S, Fun), reltool_utils:reply(ReplyTo, Ref, Status2), ?MODULE:loop(S3); {call, ReplyTo, Ref, get_sys} -> reltool_utils:reply(ReplyTo, Ref, {ok, Sys#sys{apps = undefined}}), ?MODULE:loop(S); {call, ReplyTo, Ref, {set_sys, Sys2}} -> - S2 = S#state{sys = Sys2#sys{apps = Sys#sys.apps}}, - {S3, Status} = refresh_and_analyse(S, S2, {ok,[]}), + Fun = fun() -> S#state{sys = Sys2#sys{apps = Sys#sys.apps}} end, + {S3, Status} = config_and_refresh(S, Fun), reltool_utils:reply(ReplyTo, Ref, Status), ?MODULE:loop(S3); {call, ReplyTo, Ref, get_status} -> @@ -416,24 +412,19 @@ loop(#state{common = C, sys = Sys} = S) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% do_set_apps(#state{sys = Sys} = S, ChangedApps) -> %% Create new list of configured applications - {SysApps,Status} = app_update_config(ChangedApps, Sys#sys.apps, {ok,[]}), - {S#state{sys = Sys#sys{apps = SysApps}}, Status}. + SysApps = app_update_config(ChangedApps, Sys#sys.apps), + S#state{sys = Sys#sys{apps = SysApps}}. %% 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([#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) -> +app_update_config([#app{name=Name,is_escript={inlined,Escript}}|_],_SysApps) -> + reltool_utils:throw_error("Application ~p is inlined in ~p. Can not change " + "configuration for an inlined application.", + [Name,Escript]); +app_update_config([Config|Configs],SysApps) -> NewSysApps = case app_set_config_only(Config) of {delete,Name} -> @@ -441,9 +432,9 @@ app_update_config([Config|Configs],SysApps,Status) -> New -> lists:ukeymerge(#app.name,[New],SysApps) end, - app_update_config(Configs,NewSysApps,Status); -app_update_config([],SysApps,Status) -> - {SysApps,Status}. + app_update_config(Configs,NewSysApps); +app_update_config([],SysApps) -> + SysApps. app_set_config_only(#app{mods=ConfigMods} = Config) -> app_set_config_only(mod_set_config_only(ConfigMods),Config). @@ -522,7 +513,7 @@ mod_set_config_only(ConfigMods) -> analyse(#state{common=C, sys=Sys}, Apps, Status) -> %% Create a list of {RelName,AppName}, one element for each %% AppName that needs to be included for the given release. - {RelApps, Status2} = apps_in_rels(Sys#sys.rels, Apps, Status), + RelApps = apps_in_rels(Sys#sys.rels, Apps), %% Initiate is_pre_included and is_included for all applications %% based on #sys.incl_cond, #app.incl_cond and if the application @@ -531,7 +522,7 @@ analyse(#state{common=C, sys=Sys}, Apps, Status) -> %% are no duplicated module names (in different applications) %% where we can not decide which one to use. %% Write all #app to app_tab and all #mod to mod_tab. - Status3 = apps_init_is_included(C, Sys, Apps, RelApps, Status2), + Status2 = apps_init_is_included(C, Sys, Apps, RelApps, Status), %% For each module that has #mod.is_included==true, propagate %% is_included to the modules it uses. @@ -548,53 +539,50 @@ analyse(#state{common=C, sys=Sys}, Apps, Status) -> %% #mod.is_included==true for at least one module in the app. %% Set status=missing|ok for #app and #mod - indicates if module %% (.beam file) is missing in file system. - Status4 = app_recap_dependencies(C, Status3), + app_recap_dependencies(C), %% Check that the boot_rel exists. %% Check that all applications that are listed in a 'rel' spec are %% also really included in the target release. %% Check that all mandatory applications are included in all rels. - verify_config(C, Sys, RelApps, Status4). + verify_config(C, Sys, RelApps, Status2). -apps_in_rels(Rels, Apps, Status) -> - {AllRelApps, Status2} = - lists:foldl(fun(Rel, {RelApps, S}) -> - {MoreRelApps, S2} = apps_in_rel(Rel, Apps, S), - {MoreRelApps ++ RelApps, S2} +apps_in_rels(Rels, Apps) -> + AllRelApps = + lists:foldl(fun(Rel, RelApps) -> + MoreRelApps = apps_in_rel(Rel, Apps), + MoreRelApps ++ RelApps end, - {[], Status}, + [], Rels), - {lists:reverse(AllRelApps), Status2}. + lists:reverse(AllRelApps). -apps_in_rel(#rel{name = RelName, rel_apps = RelApps}, Apps, Status) -> +apps_in_rel(#rel{name = RelName, rel_apps = RelApps}, Apps) -> Mandatory = [{RelName, kernel}, {RelName, stdlib}], Other = [{RelName, AppName} || RA <- RelApps, AppName <- [RA#rel_app.name | RA#rel_app.incl_apps], not lists:keymember(AppName, 2, Mandatory)], - more_apps_in_rels(Mandatory ++ Other, Apps, [], Status). + more_apps_in_rels(Mandatory ++ Other, Apps, []). -more_apps_in_rels([{RelName, AppName} = RA | RelApps], Apps, Acc, Status) -> +more_apps_in_rels([{RelName, AppName} = RA | RelApps], Apps, Acc) -> case lists:member(RA, Acc) of true -> - more_apps_in_rels(RelApps, Apps, Acc, Status); + more_apps_in_rels(RelApps, Apps, Acc); false -> case lists:keyfind(AppName, #app.name, Apps) of #app{info = #app_info{applications = InfoApps}} -> Extra = [{RelName, N} || N <- InfoApps], - {Acc2, Status2} = - more_apps_in_rels(Extra, Apps, [RA | Acc], Status), - more_apps_in_rels(RelApps, Apps, Acc2, Status2); + Acc2 = more_apps_in_rels(Extra, Apps, [RA | Acc]), + more_apps_in_rels(RelApps, Apps, Acc2); false -> - Text = lists:concat(["Release ", RelName, - " uses non existing application ", - AppName]), - Status2 = reltool_utils:return_first_error(Status, Text), - more_apps_in_rels(RelApps, Apps, Acc, Status2) + reltool_utils:throw_error( + "Release ~p uses non existing application ~p", + [RelName,AppName]) end end; -more_apps_in_rels([], _Apps, Acc, Status) -> - {Acc, Status}. +more_apps_in_rels([], _Apps, Acc) -> + Acc. apps_init_is_included(C, Sys, Apps, RelApps, Status) -> @@ -627,11 +615,9 @@ app_init_is_included(C, {exclude, []} -> {undefined, false, false, Status}; {exclude, [RelName | _]} -> % App is included in at least one rel - Text = lists:concat(["Application ", AppName, " is used " - "in release ", RelName, " and cannot " - "be excluded"]), - TmpStatus = reltool_utils:return_first_error(Status, Text), - {undefined, false, false, TmpStatus}; + reltool_utils:throw_error( + "Application ~p is used in release ~p and cannot be excluded", + [AppName,RelName]); {derived, []} -> {undefined, undefined, undefined, Status}; {derived, [_ | _]} -> % App is included in at least one rel @@ -651,7 +637,7 @@ app_init_is_included(C, is_pre_included = IsPreIncl, is_included = IsIncl, rels = Rels}, - ets:insert(C#common.app_tab, A2), %%! Set mods to only mod names here???? + ets:insert(C#common.app_tab, A2), Status3. mod_init_is_included(C, M, ModCond, AppCond, Default, Status) -> @@ -716,15 +702,10 @@ mod_init_is_included(C, M, ModCond, AppCond, Default, Status) -> %% Don't insert in mod_tab - using Existing reltool_utils:add_warning(Status,Warning); {_,_} -> - Error = - lists:concat( - ["Module ",M#mod.name, - " potentially included by ", - "two different applications: ", - Existing#mod.app_name, " and ", - M#mod.app_name, "."]), - %% Don't insert in mod_tab - using Existing - reltool_utils:return_first_error(Status,Error) + reltool_utils:throw_error( + "Module ~p potentially included by two different " + "applications: ~p and ~p.", + [M#mod.name,Existing#mod.app_name,M#mod.app_name]) end; [] -> ets:insert(C#common.mod_tab, M2), @@ -851,16 +832,12 @@ propagate_is_used_by(C) -> get_all_mods_and_dependencies(C)). -app_recap_dependencies(C, Status0) -> - ets:foldl(fun(App,Status) -> - app_recap_dependencies(C,App,Status) - end, - Status0, - C#common.app_tab). +app_recap_dependencies(C) -> + ets:foldl(fun(App,_) -> app_recap_dependencies(C,App) end, + ok, C#common.app_tab). -app_recap_dependencies(C, #app{mods = Mods, is_included = IsIncl} = A, Status) -> - {Mods2, IsIncl2, Status2} = - mod_recap_dependencies(C, A, Mods, [], IsIncl, Status), +app_recap_dependencies(C, #app{mods = Mods, is_included = IsIncl} = A) -> + {Mods2, IsIncl2} = mod_recap_dependencies(C, A, Mods, [], IsIncl), AppStatus = case lists:keymember(missing, #mod.status, Mods2) of true -> missing; @@ -885,9 +862,9 @@ app_recap_dependencies(C, #app{mods = Mods, is_included = IsIncl} = A, Status) - used_by_apps = UsedByApps2, is_included = IsIncl2}, ets:insert(C#common.app_tab,A2), - Status2. + ok. -mod_recap_dependencies(C, A, [#mod{name = ModName}=M1 | Mods], Acc, IsIncl, Status) -> +mod_recap_dependencies(C, A, [#mod{name = ModName}=M1 | Mods], Acc, IsIncl) -> case ets:lookup(C#common.mod_tab, ModName) of [M2] when M2#mod.app_name=:=A#app.name -> ModStatus = do_get_status(M2), @@ -903,25 +880,21 @@ mod_recap_dependencies(C, A, [#mod{name = ModName}=M1 | Mods], Acc, IsIncl, Stat {IsIncl, M2#mod{status = ModStatus, used_by_mods = []}} end, ets:insert(C#common.mod_tab, M3), - mod_recap_dependencies(C, A, Mods, [M3 | Acc], IsIncl2, Status); - [_] when A#app.is_included==false; M1#mod.incl_cond==exclude -> %!!! incl_cond could be read from #sys.app.mods + mod_recap_dependencies(C, A, Mods, [M3 | Acc], IsIncl2); + [_] when A#app.is_included==false; M1#mod.incl_cond==exclude -> %% App is explicitely excluded so it is ok that the module %% record does not exist for this module in this %% application. - mod_recap_dependencies(C, A, Mods, [M1 | Acc], IsIncl, Status); + mod_recap_dependencies(C, A, Mods, [M1 | Acc], IsIncl); [M2] -> %% A module is potensially included by multiple %% applications. This is not allowed! - Error = - lists:concat( - ["Module ",ModName, - " potentially included by two different applications: ", - A#app.name, " and ", M2#mod.app_name, "."]), - Status2 = reltool_utils:return_first_error(Status,Error), - mod_recap_dependencies(C, A, Mods, [M1 | Acc], IsIncl, Status2) + reltool_utils:throw_error( + "Module ~p potentially included by two different applications: " + "~p and ~p", [ModName,A#app.name, " and ", M2#mod.app_name, "."]) end; -mod_recap_dependencies(_C, _A, [], Acc, IsIncl, Status) -> - {lists:reverse(Acc), IsIncl, Status}. +mod_recap_dependencies(_C, _A, [], Acc, IsIncl) -> + {lists:reverse(Acc), IsIncl}. do_get_status(M) -> if @@ -944,9 +917,8 @@ verify_config(C, #sys{boot_rel = BootRel, rels = Rels}, RelApps, Status) -> Status2, Rels); false -> - Text = lists:concat(["Release ", BootRel, - " is mandatory (used as boot_rel)"]), - reltool_utils:return_first_error(Status, Text) + reltool_utils:throw_error( + "Release ~p is mandatory (used as boot_rel)",[BootRel]) end. check_app(C, {RelName, AppName}, Status) -> @@ -955,10 +927,8 @@ check_app(C, {RelName, AppName}, Status) -> when IsPreIncl; IsIncl -> Status; _ -> - Text = lists:concat(["Release ", RelName, - " uses non included application ", - AppName]), - reltool_utils:return_first_error(Status, Text) + reltool_utils:throw_error( + "Release ~p uses non included application ~p",[RelName,AppName]) end. check_rel(RelName, RelApps, Status) -> @@ -968,11 +938,9 @@ check_rel(RelName, RelApps, Status) -> true -> Acc; false -> - Text = lists:concat(["Mandatory application ", - AppName, - " is not included in release ", - RelName]), - reltool_utils:return_first_error(Acc, Text) + reltool_utils:throw_error( + "Mandatory application ~p is not included in " + "release ~p", [AppName,RelName]) end end, Mandatory = [kernel, stdlib], @@ -1033,7 +1001,7 @@ refresh_app(#app{name = AppName, %% Add optional user config for each module. %% The #mod records that are already in the #app record at %% this point do only contain user defined configuration - %% (set by parse_options/1). So here we merge with the + %% (set by parse_options/2). So here we merge with the %% default records from above. Mods2 = add_mod_config(MissingMods ++ EbinMods, Mods), @@ -1311,105 +1279,83 @@ do_save_config(S, Filename, InclDef, InclDeriv) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% do_load_config(S, SysConfig) -> - {NewSys, Status} = read_config(default_sys(), SysConfig, {ok, []}), - {S#state{sys = NewSys}, Status}. + S#state{sys = read_config(default_sys(), SysConfig)}. -read_config(OldSys, Filename, Status) when is_list(Filename) -> +read_config(OldSys, Filename) when is_list(Filename) -> case file:consult(Filename) of {ok, [SysConfig | _]} -> - read_config(OldSys, SysConfig, Status); + read_config(OldSys, SysConfig); {ok, Content} -> - Text = lists:flatten(io_lib:format("~p", [Content])), - {OldSys, - reltool_utils:return_first_error(Status, - "Illegal file content: " ++ - Text)}; + reltool_utils:throw_error("Illegal file content: ~p",[Content]); {error, Reason} -> - Text = file:format_error(Reason), - {OldSys, - reltool_utils:return_first_error(Status, - "Illegal config file " ++ - Filename ++ ": " ++ Text)} + reltool_utils:throw_error("Illegal config file ~p: ~s", + [Filename,file:format_error(Reason)]) end; -read_config(OldSys, {sys, KeyVals}, Status) -> - {NewSys, Status2} = - decode(OldSys#sys{apps = [], rels = []}, KeyVals, Status), - case Status2 of - {ok, _Warnings} -> % BUGBUG: handle warnings - Apps = [A#app{mods = lists:sort(A#app.mods)} || - A <- NewSys#sys.apps], - case NewSys#sys.rels of - [] -> Rels = reltool_utils:default_rels(); - Rels -> ok - end, - NewSys2 = NewSys#sys{apps = lists:sort(Apps), - rels = lists:sort(Rels)}, - case lists:keymember(NewSys2#sys.boot_rel, - #rel.name, - NewSys2#sys.rels) of - true -> - {NewSys2, Status2}; - false -> - Text2 = lists:concat(["Release " ++ NewSys2#sys.boot_rel, - " is mandatory (used as boot_rel)"]), - {OldSys, reltool_utils:return_first_error(Status2, Text2)} - end; - {error, _} -> - %% Keep old state - {OldSys, Status2} +read_config(OldSys, {sys, KeyVals}) -> + NewSys = decode(OldSys#sys{apps = [], rels = []}, KeyVals), + Apps = [A#app{mods = lists:sort(A#app.mods)} || A <- NewSys#sys.apps], + Rels = + case NewSys#sys.rels of + [] -> reltool_utils:default_rels(); + Rs -> Rs + end, + NewSys2 = NewSys#sys{apps = lists:sort(Apps), + rels = lists:sort(Rels)}, + case lists:keymember(NewSys2#sys.boot_rel, #rel.name, NewSys2#sys.rels) of + true -> + NewSys2; + false -> + reltool_utils:throw_error( + "Release ~p is mandatory (used as boot_rel)", + [NewSys2#sys.boot_rel]) end; -read_config(OldSys, BadConfig, Status) -> - Text = lists:flatten(io_lib:format("~p", [BadConfig])), - {OldSys, - reltool_utils:return_first_error(Status, "Illegal content: " ++ Text)}. +read_config(_OldSys, BadConfig) -> + reltool_utils:throw_error("Illegal content: ~p", [BadConfig]). -decode(#sys{apps = Apps} = Sys, [{erts = Name, AppKeyVals} | SysKeyVals], - Status) +decode(#sys{apps = Apps} = Sys, [{erts = Name, AppKeyVals} | SysKeyVals]) when is_atom(Name), is_list(AppKeyVals) -> App = default_app(Name), - {App2, Status2} = decode(App, AppKeyVals, Status), - decode(Sys#sys{apps = [App2 | Apps]}, SysKeyVals, Status2); -decode(#sys{apps = Apps} = Sys, [{app, Name, AppKeyVals} | SysKeyVals], Status) + App2= decode(App, AppKeyVals), + decode(Sys#sys{apps = [App2 | Apps]}, SysKeyVals); +decode(#sys{apps = Apps} = Sys, [{app, Name, AppKeyVals} | SysKeyVals]) when is_atom(Name), is_list(AppKeyVals) -> App = default_app(Name), - {App2, Status2} = decode(App, AppKeyVals, Status), - decode(Sys#sys{apps = [App2 | Apps]}, SysKeyVals, Status2); + App2 = decode(App, AppKeyVals), + decode(Sys#sys{apps = [App2 | Apps]}, SysKeyVals); decode(#sys{apps = Apps, escripts = Escripts} = Sys, - [{escript, File0, AppKeyVals} | SysKeyVals], Status) + [{escript, File0, AppKeyVals} | SysKeyVals]) when is_list(File0), is_list(AppKeyVals) -> File = filename:absname(File0), App = default_escript_app(File), - {App2, Status2} = decode(App, AppKeyVals, Status), + App2 = decode(App, AppKeyVals), decode(Sys#sys{apps = [App2 | Apps], escripts = [File | Escripts]}, - SysKeyVals, - Status2); -decode(#sys{rels = Rels} = Sys, [{rel, Name, Vsn, RelApps} | SysKeyVals], - Status) + SysKeyVals); +decode(#sys{rels = Rels} = Sys, [{rel, Name, Vsn, RelApps} | SysKeyVals]) when is_list(Name), is_list(Vsn), is_list(RelApps) -> Rel = #rel{name = Name, vsn = Vsn, rel_apps = []}, - {Rel2, Status2} = decode(Rel, RelApps, Status), - decode(Sys#sys{rels = [Rel2 | Rels]}, SysKeyVals, Status2); -decode(#sys{} = Sys, [{Key, Val} | KeyVals], Status) -> - {Sys3, Status3} = + Rel2 = decode(Rel, RelApps), + decode(Sys#sys{rels = [Rel2 | Rels]}, SysKeyVals); +decode(#sys{} = Sys, [{Key, Val} | KeyVals]) -> + Sys3 = case Key of root_dir when is_list(Val) -> - {Sys#sys{root_dir = Val}, Status}; + Sys#sys{root_dir = Val}; lib_dirs when is_list(Val) -> - {Sys#sys{lib_dirs = Val}, Status}; + Sys#sys{lib_dirs = Val}; mod_cond when Val =:= all; Val =:= app; Val =:= ebin; Val =:= derived; Val =:= none -> - {Sys#sys{mod_cond = Val}, Status}; + Sys#sys{mod_cond = Val}; incl_cond when Val =:= include; Val =:= exclude; Val =:= derived -> - {Sys#sys{incl_cond = Val}, Status}; + Sys#sys{incl_cond = Val}; boot_rel when is_list(Val) -> - {Sys#sys{boot_rel = Val}, Status}; + Sys#sys{boot_rel = Val}; emu_name when is_list(Val) -> - {Sys#sys{emu_name = Val}, Status}; + Sys#sys{emu_name = Val}; profile when Val =:= development; Val =:= embedded; Val =:= standalone -> @@ -1418,166 +1364,126 @@ decode(#sys{} = Sys, [{Key, Val} | KeyVals], Status) -> InclApp = reltool_utils:choose_default(incl_app_filters, Val, false), ExclApp = reltool_utils:choose_default(excl_app_filters, Val, false), AppType = reltool_utils:choose_default(embedded_app_type, Val, false), - {Sys#sys{profile = Val, - incl_sys_filters = dec_re(incl_sys_filters, - InclSys, - Sys#sys.incl_sys_filters), - excl_sys_filters = dec_re(excl_sys_filters, - ExclSys, - Sys#sys.excl_sys_filters), - incl_app_filters = dec_re(incl_app_filters, - InclApp, - Sys#sys.incl_app_filters), - excl_app_filters = dec_re(excl_app_filters, - ExclApp, - Sys#sys.excl_app_filters), - embedded_app_type = AppType}, - Status}; + Sys#sys{profile = Val, + incl_sys_filters = dec_re(incl_sys_filters, + InclSys, + Sys#sys.incl_sys_filters), + excl_sys_filters = dec_re(excl_sys_filters, + ExclSys, + Sys#sys.excl_sys_filters), + incl_app_filters = dec_re(incl_app_filters, + InclApp, + Sys#sys.incl_app_filters), + excl_app_filters = dec_re(excl_app_filters, + ExclApp, + Sys#sys.excl_app_filters), + embedded_app_type = AppType}; incl_sys_filters -> - {Sys#sys{incl_sys_filters = - dec_re(Key, - Val, - Sys#sys.incl_sys_filters)}, - Status}; + Sys#sys{incl_sys_filters = + dec_re(Key, Val, Sys#sys.incl_sys_filters)}; excl_sys_filters -> - {Sys#sys{excl_sys_filters = - dec_re(Key, - Val, - Sys#sys.excl_sys_filters)}, - Status}; + Sys#sys{excl_sys_filters = + dec_re(Key, Val, Sys#sys.excl_sys_filters)}; incl_app_filters -> - {Sys#sys{incl_app_filters = - dec_re(Key, - Val, - Sys#sys.incl_app_filters)}, - Status}; + Sys#sys{incl_app_filters = + dec_re(Key, Val, Sys#sys.incl_app_filters)}; excl_app_filters -> - {Sys#sys{excl_app_filters = - dec_re(Key, - Val, - Sys#sys.excl_app_filters)}, - Status}; + Sys#sys{excl_app_filters = + dec_re(Key, Val, Sys#sys.excl_app_filters)}; incl_archive_filters -> - {Sys#sys{incl_archive_filters = - dec_re(Key, - Val, - Sys#sys.incl_archive_filters)}, - Status}; + Sys#sys{incl_archive_filters = + dec_re(Key, Val, Sys#sys.incl_archive_filters)}; excl_archive_filters -> - {Sys#sys{excl_archive_filters = - dec_re(Key, - Val, - Sys#sys.excl_archive_filters)}, - Status}; + Sys#sys{excl_archive_filters = + dec_re(Key, Val, Sys#sys.excl_archive_filters)}; archive_opts when is_list(Val) -> - {Sys#sys{archive_opts = Val}, Status}; + Sys#sys{archive_opts = Val}; relocatable when Val =:= true; Val =:= false -> - {Sys#sys{relocatable = Val}, Status}; + Sys#sys{relocatable = Val}; rel_app_type when Val =:= permanent; Val =:= transient; Val =:= temporary; Val =:= load; Val =:= none -> - {Sys#sys{rel_app_type = Val}, Status}; + Sys#sys{rel_app_type = Val}; embedded_app_type when Val =:= permanent; Val =:= transient; Val =:= temporary; Val =:= load; Val =:= none; Val =:= undefined -> - {Sys#sys{embedded_app_type = Val}, Status}; + Sys#sys{embedded_app_type = Val}; app_file when Val =:= keep; Val =:= strip; Val =:= all -> - {Sys#sys{app_file = Val}, Status}; + Sys#sys{app_file = Val}; debug_info when Val =:= keep; Val =:= strip -> - {Sys#sys{debug_info = Val}, Status}; + Sys#sys{debug_info = Val}; _ -> - Text = lists:flatten(io_lib:format("~p", [{Key, Val}])), - {Sys, reltool_utils:return_first_error(Status, - "Illegal option: " ++ - Text)} + reltool_utils:throw_error("Illegal option: ~p", [{Key, Val}]) end, - decode(Sys3, KeyVals, Status3); -decode(#app{} = App, [{Key, Val} | KeyVals], Status) -> - {App2, Status2} = + decode(Sys3, KeyVals); +decode(#app{} = App, [{Key, Val} | KeyVals]) -> + App2 = case Key of mod_cond when Val =:= all; Val =:= app; Val =:= ebin; Val =:= derived; Val =:= none -> - {App#app{mod_cond = Val}, Status}; + App#app{mod_cond = Val}; incl_cond when Val =:= include; Val =:= exclude; Val =:= derived -> - {App#app{incl_cond = Val}, Status}; + App#app{incl_cond = Val}; debug_info when Val =:= keep; Val =:= strip -> - {App#app{debug_info = Val}, Status}; + App#app{debug_info = Val}; app_file when Val =:= keep; Val =:= strip; Val =:= all -> - {App#app{app_file = Val}, Status}; + App#app{app_file = Val}; app_type when Val =:= permanent; Val =:= transient; Val =:= temporary; Val =:= load; Val =:= none; Val =:= undefined -> - {App#app{app_type = Val}, Status}; + App#app{app_type = Val}; incl_app_filters -> - {App#app{incl_app_filters = - dec_re(Key, - Val, - App#app.incl_app_filters)}, - Status}; + App#app{incl_app_filters = + dec_re(Key, Val, App#app.incl_app_filters)}; excl_app_filters -> - {App#app{excl_app_filters = - dec_re(Key, - Val, - App#app.excl_app_filters)}, - Status}; + App#app{excl_app_filters = + dec_re(Key, Val, App#app.excl_app_filters)}; incl_archive_filters -> - {App#app{incl_archive_filters = - dec_re(Key, - Val, - App#app.incl_archive_filters)}, - Status}; + App#app{incl_archive_filters = + dec_re(Key, Val, App#app.incl_archive_filters)}; excl_archive_filters -> - {App#app{excl_archive_filters = - dec_re(Key, - Val, - App#app.excl_archive_filters)}, - Status}; + App#app{excl_archive_filters = + dec_re(Key, Val, App#app.excl_archive_filters)}; archive_opts when is_list(Val) -> - {App#app{archive_opts = Val}, Status}; + App#app{archive_opts = Val}; vsn when is_list(Val) -> - {App#app{use_selected_vsn = true, vsn = Val}, Status}; + App#app{use_selected_vsn = true, vsn = Val}; _ -> - Text = lists:flatten(io_lib:format("~p", [{Key, Val}])), - {App, reltool_utils:return_first_error(Status, - "Illegal option: " ++ Text)} + reltool_utils:throw_error("Illegal option: ~p", [{Key, Val}]) end, - decode(App2, KeyVals, Status2); -decode(#app{mods = Mods} = App, [{mod, Name, ModKeyVals} | AppKeyVals], - Status) -> - {Mod, Status2} = decode(#mod{name = Name}, ModKeyVals, Status), - decode(App#app{mods = [Mod | Mods]}, AppKeyVals, Status2); -decode(#mod{} = Mod, [{Key, Val} | KeyVals], Status) -> - {Mod2, Status2} = + decode(App2, KeyVals); +decode(#app{mods = Mods} = App, [{mod, Name, ModKeyVals} | AppKeyVals]) -> + Mod = decode(#mod{name = Name}, ModKeyVals), + decode(App#app{mods = [Mod | Mods]}, AppKeyVals); +decode(#mod{} = Mod, [{Key, Val} | KeyVals]) -> + Mod2 = case Key of incl_cond when Val =:= include; Val =:= exclude; Val =:= derived -> - {Mod#mod{incl_cond = Val}, Status}; + Mod#mod{incl_cond = Val}; debug_info when Val =:= keep; Val =:= strip -> - {Mod#mod{debug_info = Val}, Status}; + Mod#mod{debug_info = Val}; _ -> - Text = lists:flatten(io_lib:format("~p", [{Key, Val}])), - {Mod, - reltool_utils:return_first_error(Status, - "Illegal option: " ++ Text)} + reltool_utils:throw_error("Illegal option: ~p", [{Key, Val}]) end, - decode(Mod2, KeyVals, Status2); -decode(#rel{rel_apps = RelApps} = Rel, [RelApp | KeyVals], Status) -> + decode(Mod2, KeyVals); +decode(#rel{rel_apps = RelApps} = Rel, [RelApp | KeyVals]) -> {ValidTypesAssigned, RA} = case RelApp of Name when is_atom(Name) -> @@ -1597,19 +1503,14 @@ decode(#rel{rel_apps = RelApps} = Rel, [RelApp | KeyVals], Status) -> end, case ValidTypesAssigned of true -> - decode(Rel#rel{rel_apps = RelApps ++ [RA]}, KeyVals, Status); + decode(Rel#rel{rel_apps = RelApps ++ [RA]}, KeyVals); false -> - Text = lists:flatten(io_lib:format("~p", [RelApp])), - Status2 = - reltool_utils:return_first_error(Status, - "Illegal option: " ++ Text), - decode(Rel, KeyVals, Status2) + reltool_utils:throw_error("Illegal option: ~p", [RelApp]) end; -decode(Acc, [], Status) -> - {Acc, Status}; -decode(Acc, KeyVal, Status) -> - Text = lists:flatten(io_lib:format("~p", [KeyVal])), - {Acc, reltool_utils:return_first_error(Status, "Illegal option: " ++ Text)}. +decode(Acc, []) -> + Acc; +decode(_Acc, KeyVal) -> + reltool_utils:throw_error("Illegal option: ~p", [KeyVal]). is_type(Type) -> case Type of @@ -1638,13 +1539,13 @@ default_escript_app(File) -> %% Apps is a list of #app records - sorted on #app.name - containing %% only the apps that have specific configuration (e.g. in the config %% file) -refresh(#state{sys=Sys} = S, Status) -> +refresh(#state{sys=Sys} = S) -> RootDir = filename:absname(Sys#sys.root_dir), LibDirs = [filename:absname(D) || D <- Sys#sys.lib_dirs], Escripts = [filename:absname(E) || E <- Sys#sys.escripts], %% Read all lib dirs and return sorted [{AppName,Dir}] - {SourceDirs, Status2} = libs_to_dirs(RootDir, LibDirs, Status), + SourceDirs = libs_to_dirs(RootDir, LibDirs), %% Create #app records for all apps in SourceDirs, and merge with %% list of apps from config. @@ -1652,24 +1553,24 @@ refresh(#state{sys=Sys} = S, Status) -> %% For each escript, find all related files and convert to #app %% and #mod records - {AllApps, Status3} = escripts_to_apps(Escripts, MergedApps, Status2), + {AllApps, Status2} = escripts_to_apps(Escripts, MergedApps, {ok,[]}), %% Make sure correct version of each application is used according %% to the user configuration. %% Then find all modules and their dependencies and set user %% configuration per module if it exists. - {RefreshedApps, Status4} = refresh_apps(Sys#sys.apps, AllApps, [], - true, Status3), + {RefreshedApps, Status3} = refresh_apps(Sys#sys.apps, AllApps, [], + true, Status2), %% Make sure erts exists in app list and has a version (or warn) - {PatchedApps, Status5} = patch_erts_version(RootDir, RefreshedApps, Status4), + {PatchedApps, Status4} = patch_erts_version(RootDir, RefreshedApps, Status3), %% Update #sys and return Escripts2 = [A#app.active_dir || A <- PatchedApps, A#app.is_escript], Sys2 = Sys#sys{root_dir = RootDir, lib_dirs = LibDirs, escripts = Escripts2}, - {S#state{sys=Sys2}, PatchedApps, Status5}. + {S#state{sys=Sys2}, PatchedApps, Status4}. patch_erts_version(RootDir, Apps, Status) -> AppName = erts, @@ -1689,12 +1590,11 @@ patch_erts_version(RootDir, Apps, Status) -> {Apps, Status} end; false -> - Text = "erts cannot be found in the root directory " ++ RootDir, - Status2 = reltool_utils:return_first_error(Status, Text), - {Apps, Status2} + reltool_utils:throw_error( + "erts cannot be found in the root directory ~p", [RootDir]) end. -libs_to_dirs(RootDir, LibDirs, Status) -> +libs_to_dirs(RootDir, LibDirs) -> case file:list_dir(RootDir) of {ok, RootFiles} -> RootLibDir = filename:join([RootDir, "lib"]), @@ -1716,21 +1616,16 @@ libs_to_dirs(RootDir, LibDirs, Status) -> end, ErtsFiles = [{erts, Fun(F)} || F <- RootFiles, lists:prefix("erts", F)], - app_dirs2(AllLibDirs, [ErtsFiles], Status); + app_dirs2(AllLibDirs, [ErtsFiles]); [Duplicate | _] -> - {[], - reltool_utils:return_first_error(Status, - "Duplicate library: " ++ - Duplicate)} + reltool_utils:throw_error("Duplicate library: ~p",[Duplicate]) end; {error, Reason} -> - Text = file:format_error(Reason), - {[], reltool_utils:return_first_error(Status, - "Missing root library " ++ - RootDir ++ ": " ++ Text)} + reltool_utils:throw_error("Missing root library ~p: ~s", + [RootDir,file:format_error(Reason)]) end. -app_dirs2([Lib | Libs], Acc, Status) -> +app_dirs2([Lib | Libs], Acc) -> case file:list_dir(Lib) of {ok, Files} -> Filter = @@ -1750,15 +1645,13 @@ app_dirs2([Lib | Libs], Acc, Status) -> end end, Files2 = lists:zf(Filter, Files), - app_dirs2(Libs, [Files2 | Acc], Status); + app_dirs2(Libs, [Files2 | Acc]); {error, Reason} -> - Text = file:format_error(Reason), - {[], reltool_utils:return_first_error(Status, - "Illegal library " ++ - Lib ++ ": " ++ Text)} + reltool_utils:throw_error("Illegal library ~p: ~s", + [Lib, file:format_error(Reason)]) end; -app_dirs2([], Acc, Status) -> - {lists:sort(lists:append(Acc)), Status}. +app_dirs2([], Acc) -> + lists:sort(lists:append(Acc)). escripts_to_apps([Escript | Escripts], Apps, Status) -> {EscriptAppName, _Label} = split_escript_name(Escript), @@ -1836,10 +1729,7 @@ escripts_to_apps([Escript | Escripts], Apps, Status) -> Status2), escripts_to_apps(Escripts, Apps2, Status3); {error, Reason} -> - Text = lists:flatten(io_lib:format("~p", [Reason])), - {[], reltool_utils:return_first_error(Status, - "Illegal escript " ++ - Escript ++ ": " ++ Text)} + reltool_utils:throw_error("Illegal escript ~p: ~p", [Escript,Reason]) end; escripts_to_apps([], Apps, Status) -> {Apps, Status}. @@ -1900,10 +1790,9 @@ init_escript_app(AppName, EscriptAppName, Dir, Info, Mods, Apps, Status) -> 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. ", - "Escript ", Dir," contains application ", - AppName, "."]), - {App2, reltool_utils:return_first_error(Status, Error)}; + reltool_utils:throw_error( + "~p: Application name clash. Escript ~p contains application ~p.", + [AppName,Dir,AppName]); false -> {App2, Status} end. @@ -1983,10 +1872,8 @@ ensure_app_info(#app{is_escript = IsEscript, active_dir = Dir, info = Info}, 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."]), - Status2 = reltool_utils:return_first_error(Status, Error), - {missing_app_info(""), undefined, Status2}; +ensure_app_info(#app{name = Name, sorted_dirs = []}, _Status) -> + reltool_utils:throw_error("~p: : Missing application directory.",[Name]); ensure_app_info(#app{name = Name, vsn = Vsn, sorted_dirs = Dirs, @@ -2008,11 +1895,10 @@ ensure_app_info(#app{name = Name, %% No redundant info Status2; [BadVsn | _] -> - Error2 = - lists:concat([Name, ": Application version clash. ", - "Multiple directories contains version \"", - BadVsn, "\"."]), - reltool_utils:return_first_error(Status2, Error2) + reltool_utils:throw_error( + "~p: Application version clash. " + "Multiple directories contains version ~p.", + [Name,BadVsn]) end, FirstInfo = hd(AllInfo), FirstDir = hd(Dirs), @@ -2026,13 +1912,9 @@ ensure_app_info(#app{name = Name, {Info, VsnDir} -> {Info, VsnDir, Status3}; false -> - Error3 = - lists:concat([Name, - ": No application directory contains ", - "selected version \"", - Vsn, "\"."]), - Status4 = reltool_utils:return_first_error(Status3, Error3), - {FirstInfo, FirstDir, Status4} + reltool_utils:throw_error( + "~p: No application directory contains selected version ~p", + [Name,Vsn]) end end; ensure_app_info(#app{active_dir = Dir, info = Info}, Status) -> @@ -2064,42 +1946,24 @@ sys_all_apps(C,Sys) -> all_apps(C) -> ets:match_object(C#common.app_tab,'_'). -refresh_and_analyse_no_rollback(#state{common=C} = S, {ok,_} = Status) -> - case refresh(S, Status) of - {S2, Apps, {ok, _}=Status2} -> - case analyse(S2, Apps, Status2) of - {ok, _} = Status3 -> - %% Set old_xxx is equal to xxx - FakeBackup = {ets:tab2list(C#common.app_tab), - ets:tab2list(C#common.mod_tab)}, - save_old(S2, S2, FakeBackup, Status3); - {error,Reason} -> - exit(Reason) - end; - {_,_,{error,Reason}} -> - exit(Reason) - end; -refresh_and_analyse_no_rollback(_,{error,Reason}) -> - exit(Reason). - -refresh_and_analyse(OldS, S, {ok,_}=Status) -> - case refresh(S, Status) of - {S2, Apps, {ok,_}=Status2} -> - %% Analyse will write to app_tab and mod_tab, so we first - %% backup these tables and clear them - Backup = backup(OldS), - case analyse(S2, Apps, Status2) of - {ok, _} = Status3 -> - save_old(OldS, S2, Backup, Status3); - Status3 -> - restore(Backup,OldS), - {OldS,Status3} - end; - {_, _, Status2} -> - {OldS, Status2} - end; -refresh_and_analyse(OldS, _S, Status) -> - {OldS,Status}. +config_and_refresh(OldS, Fun) -> + try + S = Fun(), + {S2, Apps, Status2} = refresh(S), + %% Analyse will write to app_tab and mod_tab, so we first + %% backup these tables and clear them + Backup = backup(OldS), + try + Status3 = analyse(S2, Apps, Status2), + S3 = save_old(OldS, S2, Backup, Status3), + {S3, Status3} + catch throw:{error,_} = Error1 -> + restore(Backup,OldS), + throw(Error1) + end + catch throw:{error,_} = Error2 -> + {OldS, Error2} + end. backup(#state{common=C}) -> @@ -2120,10 +1984,9 @@ save_old(#state{status=OldStatus,sys=OldSys},#state{common=C}=NewS, ets:delete_all_objects(C#common.old_mod_tab), insert_all(C#common.old_app_tab,OldApps), insert_all(C#common.old_mod_tab,OldMods), - {NewS#state{old_sys=OldSys, + NewS#state{old_sys=OldSys, old_status=OldStatus, - status=NewStatus}, - NewStatus}. + status=NewStatus}. insert_all(Tab,Items) -> lists:foreach(fun(Item) -> ets:insert(Tab,Item) end, Items). diff --git a/lib/reltool/src/reltool_sys_win.erl b/lib/reltool/src/reltool_sys_win.erl index 1638acb145..c75b302770 100644 --- a/lib/reltool/src/reltool_sys_win.erl +++ b/lib/reltool/src/reltool_sys_win.erl @@ -135,54 +135,49 @@ init(Options) -> do_init([{safe_config, Safe}, {parent, Parent} | Options]) -> case reltool_server:start_link(Options) of - {ok, ServerPid, C} -> + {ok, ServerPid, C, Sys} -> process_flag(trap_exit, C#common.trap_exit), wx:new(), wx:debug(C#common.wx_debug), %% wx_misc:beginBusyCursor(), - case reltool_server:get_status(ServerPid) of - {ok, Warnings} -> - exit_dialog(Warnings), - {ok, Sys} = reltool_server:get_sys(ServerPid), - S = #state{parent_pid = Parent, - server_pid = ServerPid, - common = C, - config_file = filename:absname("config.reltool"), - target_dir = filename:absname("reltool_target_dir"), - app_wins = [], - sys = Sys, - fgraph_wins = []}, - S2 = create_window(S), - S5 = wx:batch(fun() -> - Title = atom_to_list(?APPLICATION), - wxFrame:setTitle(S2#state.frame, - Title), - %% wxFrame:setMinSize(Frame, - %% {?WIN_WIDTH, ?WIN_HEIGHT}), - wxStatusBar:setStatusText( - S2#state.status_bar, - "Done."), - S3 = redraw_apps(S2), - S4 = redraw_libs(S3), - redraw_config_page(S4) - end), - %% wx_misc:endBusyCursor(), - %% wxFrame:destroy(Frame), - proc_lib:init_ack(S#state.parent_pid, {ok, self()}), - loop(S5); - {error, Reason} -> - restart_server_safe_config(Safe,Parent,Reason) - end; + {ok, Warnings} = reltool_server:get_status(ServerPid), + exit_dialog(Warnings), + S = #state{parent_pid = Parent, + server_pid = ServerPid, + common = C, + config_file = filename:absname("config.reltool"), + target_dir = filename:absname("reltool_target_dir"), + app_wins = [], + sys = Sys, + fgraph_wins = []}, + S2 = create_window(S), + S5 = wx:batch(fun() -> + Title = atom_to_list(?APPLICATION), + wxFrame:setTitle(S2#state.frame, + Title), + %% wxFrame:setMinSize(Frame, + %% {?WIN_WIDTH, ?WIN_HEIGHT}), + wxStatusBar:setStatusText( + S2#state.status_bar, + "Done."), + S3 = redraw_apps(S2), + S4 = redraw_libs(S3), + redraw_config_page(S4) + end), + %% wx_misc:endBusyCursor(), + %% wxFrame:destroy(Frame), + proc_lib:init_ack(S#state.parent_pid, {ok, self()}), + loop(S5); {error, Reason} -> - io:format("~p(~p): ~p\n", [?MODULE, ?LINE, Reason]), - exit(Reason) + restart_server_safe_config(Safe,Parent,Reason) end. -restart_server_safe_config(true,_Parent,Reason) -> +restart_server_safe_config(true,Parent,Reason) -> io:format("~p(~p): ~p\n", [?MODULE, ?LINE, Reason]), - exit(Reason); + proc_lib:init_ack(Parent, {error,Reason}); restart_server_safe_config(false,Parent,Reason) -> + wx:new(), Strings = [{?wxBLACK,"Could not start reltool server:\n\n"}, {?wxRED,Reason++"\n\n"}, @@ -197,7 +192,7 @@ restart_server_safe_config(false,Parent,Reason) -> do_init([{safe_config,true},{parent,Parent},?safe_config]); ?wxID_CANCEL -> io:format("~p(~p): ~p\n", [?MODULE, ?LINE, Reason]), - exit(Reason) + proc_lib:init_ack(Parent,{error,Reason}) end. exit_dialog([]) -> diff --git a/lib/reltool/src/reltool_utils.erl b/lib/reltool/src/reltool_utils.erl index 39d057d994..a9107355c7 100644 --- a/lib/reltool/src/reltool_utils.erl +++ b/lib/reltool/src/reltool_utils.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2010. 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 @@ -30,7 +30,7 @@ get_item/1, get_items/1, get_selected_items/3, select_items/3, select_item/2, - safe_keysearch/5, print/4, return_first_error/2, add_warning/2, + safe_keysearch/5, print/4, add_warning/2, create_dir/1, list_dir/1, read_file_info/1, write_file_info/2, read_file/1, write_file/2, @@ -392,31 +392,12 @@ print(X, X, Format, Args) -> print(_, _, _, _) -> ok. -%% -define(SAFE(M,F,A), safe(M, F, A, ?MODULE, ?LINE)). -%% -%% safe(M, F, A, Mod, Line) -> -%% case catch apply(M, F, A) of -%% {'EXIT', Reason} -> -%% io:format("~p(~p): ~p:~p~p -> ~p\n", [Mod, Line, M, F, A, Reason]), -%% timer:sleep(infinity); -%% Res -> -%% Res -%% end. - -return_first_error(Status, NewError) when is_list(NewError) -> - case Status of - {ok, _Warnings} -> - {error, NewError}; - {error, OldError} -> - {error, OldError} - end. - -add_warning(Status, Warning) -> - case Status of - {ok, Warnings} -> - {ok, [Warning | Warnings]}; - {error, Error} -> - {error, Error} +add_warning({ok,Warnings}, Warning) -> + case lists:member(Warning,Warnings) of + true -> + {ok,Warnings}; + false -> + {ok,[Warning|Warnings]} end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl index 2588c0829f..5526df5f52 100644 --- a/lib/reltool/test/reltool_server_SUITE.erl +++ b/lib/reltool/test/reltool_server_SUITE.erl @@ -883,8 +883,7 @@ create_standalone_app_clash(Config) -> ]}, ?msym({error,"someapp: Application name clash. Escript "++_}, - reltool:get_target_spec([{config,Sys}])), - + reltool:start_server([{config,Sys}])), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1214,7 +1213,7 @@ set_app_and_undo(Config) -> {app,tools,[{incl_cond,include}]}]}, {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), ?m({ok, Sys}, reltool:get_config(Pid)), - ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:get_status(Pid)), + ?msym({ok,["a: Cannot parse app file"++_]},reltool_server:get_status(Pid)), %% Get app and mod {ok,Tools} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), @@ -1228,7 +1227,7 @@ set_app_and_undo(Config) -> {ok,ToolsNoCover,["a: Cannot parse app file"++_|_]} = ?msym({ok,_,["a: Cannot parse app file"++_|_]}, reltool_server:set_app(Pid,Tools1)), - ?msym({ok,["a: Cannot parse app file"++_|_]}, reltool_server:get_status(Pid)), + ?msym({ok,["a: Cannot parse app file"++_]}, reltool_server:get_status(Pid)), %% Check that the module is no longer included ?m({ok,ToolsNoCover}, reltool_server:get_app(Pid,tools)), @@ -1239,13 +1238,13 @@ set_app_and_undo(Config) -> ?m(ok, reltool_server:undo_config(Pid)), ?m({ok,Tools}, reltool_server:get_app(Pid,tools)), ?m({ok,Cover}, reltool_server:get_mod(Pid,cover)), - ?msym({ok,["a: Cannot parse app file"++_|_]}, reltool_server:get_status(Pid)), + ?msym({ok,["a: Cannot parse app file"++_]}, reltool_server:get_status(Pid)), %% Undo again, to check that it toggles ?msym(ok, reltool_server:undo_config(Pid)), ?m({ok,ToolsNoCover}, reltool_server:get_app(Pid,tools)), ?m({ok,NoIncludeCover}, reltool_server:get_mod(Pid,cover)), - ?msym({ok,["a: Cannot parse app file"++_|_]}, reltool_server:get_status(Pid)), + ?msym({ok,["a: Cannot parse app file"++_]}, reltool_server:get_status(Pid)), ?m(ok, reltool:stop(Pid)), ok. @@ -1261,7 +1260,7 @@ set_apps_and_undo(Config) -> {app,tools,[{incl_cond,include}]}]}, {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), ?m({ok, Sys}, reltool:get_config(Pid)), - ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:get_status(Pid)), + ?msym({ok,["a: Cannot parse app file"++_]},reltool_server:get_status(Pid)), %% Get app and mod {ok,Tools} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), @@ -1272,9 +1271,9 @@ set_apps_and_undo(Config) -> %% Exclude one application with set_apps ExclTools = Tools#app{incl_cond=exclude}, - ?msym({ok,["a: Cannot parse app file"++_|_]}, + ?msym({ok,["a: Cannot parse app file"++_]}, reltool_server:set_apps(Pid,[ExclTools])), - ?msym({ok,["a: Cannot parse app file"++_|_]}, reltool_server:get_status(Pid)), + ?msym({ok,["a: Cannot parse app file"++_]}, reltool_server:get_status(Pid)), %% Check that the app and its modules (one of them) are no longer included {ok,NoTools} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), @@ -1287,13 +1286,13 @@ set_apps_and_undo(Config) -> ?m(ok, reltool_server:undo_config(Pid)), ?m({ok,Tools}, reltool_server:get_app(Pid,tools)), ?m({ok,Cover}, reltool_server:get_mod(Pid,cover)), - ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:get_status(Pid)), + ?msym({ok,["a: Cannot parse app file"++_]},reltool_server:get_status(Pid)), %% Undo again, to check that it toggles ?m(ok, reltool_server:undo_config(Pid)), ?m({ok,NoTools}, reltool_server:get_app(Pid,tools)), ?m({ok,NoIncludeCover}, reltool_server:get_mod(Pid,cover)), - ?msym({ok,["a: Cannot parse app file"++_|_]}, reltool_server:get_status(Pid)), + ?msym({ok,["a: Cannot parse app file"++_]}, reltool_server:get_status(Pid)), ?m(ok, reltool:stop(Pid)), ok. @@ -1386,10 +1385,10 @@ set_sys_and_undo(Config) -> NewLib = filename:join(datadir(Config),"faulty_app_file"), NewLibDirs = [NewLib | SysRec#sys.lib_dirs], NewSysRec = SysRec#sys{lib_dirs=NewLibDirs}, - ?msym({ok,["a: Cannot parse app file"++_|_]}, + ?msym({ok,["a: Cannot parse app file"++_]}, reltool_server:set_sys(Pid, NewSysRec)), ?m({ok,NewSysRec}, reltool_server:get_sys(Pid)), - ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:get_status(Pid)), + ?msym({ok,["a: Cannot parse app file"++_]},reltool_server:get_status(Pid)), %% Undo ?m(ok, reltool_server:undo_config(Pid)), @@ -1399,7 +1398,7 @@ set_sys_and_undo(Config) -> %% Undo again, to check that it toggles ?m(ok,reltool_server:undo_config(Pid)), ?m({ok,NewSysRec}, reltool_server:get_sys(Pid)), - ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:get_status(Pid)), + ?msym({ok,["a: Cannot parse app file"++_]},reltool_server:get_status(Pid)), ?m(ok, reltool:stop(Pid)), ok. @@ -1431,12 +1430,12 @@ load_config_and_undo(Config) -> {app,sasl,[{incl_cond,include}]}, {app,stdlib,[{incl_cond,include}]}, {app,tools,[{incl_cond,derived}]}]}, - ?msym({ok,["a: Cannot parse app file"++_|_]}, + ?msym({ok,["a: Cannot parse app file"++_]}, reltool_server:load_config(Pid,Sys2)), %%% OTP-0702, 15) ?m({ok, Sys2}, reltool:get_config(Pid)), %%% Note that {incl_cond,exclude} is removed compared to Sys1 - %%% config is merged, not overwritten - is this correct??? - ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:get_status(Pid)), + ?msym({ok,["a: Cannot parse app file"++_]},reltool_server:get_status(Pid)), %% Check that tools is included (since it is used by sasl) but not %% pre-included (neither included or excluded => undefined) @@ -1458,7 +1457,7 @@ load_config_and_undo(Config) -> ?m(ok, reltool_server:undo_config(Pid)), ?m({ok,Tools2}, reltool_server:get_app(Pid,tools)), ?m({ok,Cover2}, reltool_server:get_mod(Pid,cover)), - ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:get_status(Pid)), + ?msym({ok,["a: Cannot parse app file"++_]},reltool_server:get_status(Pid)), ?m(ok, reltool:stop(Pid)), ok. @@ -1487,7 +1486,7 @@ load_config_fail(_Config) -> {app,kernel,[{incl_cond,include}]}, {app,sasl,[{incl_cond,include}]}, {app,stdlib,[{incl_cond,include}]}]}, - ?msym({error,"Release faulty_rel uses non existing application xxx"}, + ?msym({error,"Release \"faulty_rel\" uses non existing application xxx"}, reltool_server:load_config(Pid,Sys2)), %% Check that a rollback is done to the old configuration @@ -1674,7 +1673,7 @@ reset_config_and_undo(Config) -> {app,tools,[{incl_cond,include}]}]}, {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys1}])), ?m({ok, Sys1}, reltool:get_config(Pid)), - ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:get_status(Pid)), + ?msym({ok,["a: Cannot parse app file"++_]},reltool_server:get_status(Pid)), %% Get app and mod {ok,Tools1} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), @@ -1704,11 +1703,10 @@ reset_config_and_undo(Config) -> reltool_server:get_mod(Pid,cover)), %% Reset - ?msym({ok,["a: Cannot parse app file"++_|_]}, - reltool_server:reset_config(Pid)), + ?msym({ok,["a: Cannot parse app file"++_]},reltool_server:reset_config(Pid)), ?m({ok,Tools1}, reltool_server:get_app(Pid,tools)), ?m({ok,Cover1}, reltool_server:get_mod(Pid,cover)), - ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:get_status(Pid)), + ?msym({ok,["a: Cannot parse app file"++_]},reltool_server:get_status(Pid)), %% Undo ?m(ok, reltool_server:undo_config(Pid)), @@ -1720,7 +1718,7 @@ reset_config_and_undo(Config) -> ?m(ok, reltool_server:undo_config(Pid)), ?m({ok,Tools1}, reltool_server:get_app(Pid,tools)), ?m({ok,Cover1}, reltool_server:get_mod(Pid,cover)), - ?msym({ok,["a: Cannot parse app file"++_|_]},reltool_server:get_status(Pid)), + ?msym({ok,["a: Cannot parse app file"++_]},reltool_server:get_status(Pid)), ?m(ok, reltool:stop(Pid)), ok. -- cgit v1.2.3 From 70b56b16177703c4721f9afbb0329b181af1de3e Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 29 Feb 2012 11:18:08 +0100 Subject: [reltool] Unset ERL_FLAGS when running escript from reltool_server_SUITE OTP-9794 Some escript tests in reltool_server_SUITE failed if ERL_FLAGS was set to enable smp. The reason is that the test expected smp_support to be false, since the argument "-smp disable" is stated in the %%!-line of the escript. However, ERL_FLAGS will override the arguments in the escript so the result could not be truested. --- lib/reltool/test/reltool_server_SUITE.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl index 5526df5f52..c1d568f019 100644 --- a/lib/reltool/test/reltool_server_SUITE.erl +++ b/lib/reltool/test/reltool_server_SUITE.erl @@ -2150,7 +2150,9 @@ run(Dir, Opts, Script, Args) -> do_run(Dir, Cmd) -> io:format("Run: ~p\n", [Cmd]), - Env = [{"PATH",Dir++":"++os:getenv("PATH")}], + Env = [{"PATH",Dir++":"++os:getenv("PATH")}, + {"ERL_FLAGS",""}, % Make sure no flags are set that can override + {"ERL_ZFLAGS",""}], % any of the flags set in the escript. Port = open_port({spawn,Cmd}, [exit_status,eof,in,{env,Env}]), Res = get_data(Port, []), receive -- cgit v1.2.3 From a3a7c3590f549c609749cff16da7d00af2c114d3 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 29 Feb 2012 15:27:34 +0100 Subject: [reltool] Cleanup test code OTP-9794 Remove some unused code from reltool test. --- lib/reltool/test/reltool_server_SUITE.erl | 20 +----------------- lib/reltool/test/reltool_test_lib.erl | 6 +----- lib/reltool/test/reltool_test_lib.hrl | 34 +------------------------------ 3 files changed, 3 insertions(+), 57 deletions(-) (limited to 'lib') diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl index c1d568f019..4b0511afaf 100644 --- a/lib/reltool/test/reltool_server_SUITE.erl +++ b/lib/reltool/test/reltool_server_SUITE.erl @@ -104,8 +104,6 @@ end_per_group(_GroupName, Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Start a server process and check that it does not crash -start_server(TestInfo) when is_atom(TestInfo) -> - reltool_test_lib:tc_info(TestInfo); start_server(_Config) -> {ok, Pid} = ?msym({ok, _}, reltool:start_server([])), Libs = lists:sort(erl_libs()), @@ -121,8 +119,6 @@ start_server(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Start a server process and check that it does not crash -set_config(TestInfo) when is_atom(TestInfo) -> - reltool_test_lib:tc_info(TestInfo); set_config(_Config) -> Libs = lists:sort(erl_libs()), Default = @@ -229,8 +225,6 @@ get_config(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% OTP-9135, test that app_file option can be set to all | keep | strip -otp_9135(TestInfo) when is_atom(TestInfo) -> - reltool_test_lib:tc_info(TestInfo); otp_9135(_Config) -> Libs = lists:sort(erl_libs()), StrippedDefaultSys = @@ -260,8 +254,6 @@ otp_9135(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Generate releases -create_release(TestInfo) when is_atom(TestInfo) -> - reltool_test_lib:tc_info(TestInfo); create_release(_Config) -> %% Configure the server RelName = "Just testing...", @@ -402,8 +394,6 @@ create_release_sort(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Generate boot scripts -create_script(TestInfo) when is_atom(TestInfo) -> - reltool_test_lib:tc_info(TestInfo); create_script(_Config) -> %% Configure the server RelName = "Just testing", @@ -647,8 +637,6 @@ systools_make_script(Name,Path) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Generate target system -create_target(TestInfo) when is_atom(TestInfo) -> - reltool_test_lib:tc_info(TestInfo); create_target(_Config) -> %% Configure the server RelName1 = "Just testing", @@ -681,8 +669,6 @@ create_target(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Generate embedded target system -create_embedded(TestInfo) when is_atom(TestInfo) -> - reltool_test_lib:tc_info(TestInfo); create_embedded(_Config) -> %% Configure the server RelName1 = "Just testing", @@ -714,8 +700,6 @@ create_embedded(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Generate standalone system -create_standalone(TestInfo) when is_atom(TestInfo) -> - reltool_test_lib:tc_info(TestInfo); create_standalone(_Config) -> %% Configure the server ExDir = code:lib_dir(reltool, examples), @@ -948,10 +932,8 @@ create_multiple_standalone(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Generate old type of target system -create_old_target(TestInfo) when is_atom(TestInfo) -> - reltool_test_lib:tc_info(TestInfo); +create_old_target(_Config) -> {skip, "Old style of target"}; create_old_target(_Config) -> - ?skip("Old style of target", []), %% Configure the server RelName1 = "Just testing", diff --git a/lib/reltool/test/reltool_test_lib.erl b/lib/reltool/test/reltool_test_lib.erl index b8bcbcd009..61f783190c 100644 --- a/lib/reltool/test/reltool_test_lib.erl +++ b/lib/reltool/test/reltool_test_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2010. 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 @@ -138,10 +138,6 @@ end_per_testcase(_Func, Config) when is_list(Config) -> reset_kill_timer(Config), Config. -%% Backwards compatible with test_server -tc_info(suite) -> []; -tc_info(doc) -> "". - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Use ?log(Format, Args) as wrapper diff --git a/lib/reltool/test/reltool_test_lib.hrl b/lib/reltool/test/reltool_test_lib.hrl index b592ebb2f0..0dfc08b81c 100644 --- a/lib/reltool/test/reltool_test_lib.hrl +++ b/lib/reltool/test/reltool_test_lib.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2010. 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 @@ -18,20 +18,10 @@ -include_lib("wx/include/wx.hrl"). --define(flat_format(Format,Args), lists:flatten(io_lib:format(Format,Args))). -define(log(Format,Args), reltool_test_lib:log(Format,Args,?FILE,?LINE)). --define(warning(Format,Args), ?log("\n " ++ Format,Args)). -define(error(Format,Args), reltool_test_lib:error(Format,Args,?FILE,?LINE)). -define(verbose(Format,Args), reltool_test_lib:verbose(Format,Args,?FILE,?LINE)). --define(fatal(Format,Args), - ?error(Format, Args), - exit({test_case_fatal, Format, Args, ?FILE, ?LINE})). - --define(skip(Format,Args), - ?warning(Format, Args), - exit({skipped, ?flat_format(Format, Args)})). - -define(ignore(Expr), fun() -> AcTuAlReS = (catch (Expr)), @@ -68,25 +58,3 @@ AcTuAlReS end end()). - --define(m_receive(ExpectedMsg), - ?m(ExpectedMsg,reltool_test_lib:pick_msg())). - --define(m_multi_receive(ExpectedMsgs), - fun() -> - TmPeXpCtEdMsGs = lists:sort(ExpectedMsgs), - AcTuAlReS = - lists:sort(lists:map(fun(_) -> - reltool_test_lib:pick_msg() - end, TmPeXpCtEdMsGs)), - case AcTuAlReS of - TmPeXpCtEdMsGs -> - ?verbose("ok: ~p\n",[AcTuAlReS]), - AcTuAlReS; - _ -> - reltool_test_lib:error("Not matching actual result was:\n ~p \nExpected ~p\n", - [AcTuAlReS, ExpectedMsgs], - ?FILE, ?LINE), - AcTuAlReS - end - end()). -- cgit v1.2.3 From 2be5415d4a5d024c223d340381ef74e3697fc7ea Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 29 Feb 2012 16:30:13 +0100 Subject: [reltool] Display warnings continously in GUI OTP-9967 All active warnings are now displayed in a specific warning list at the bottom of the sys windows. Warnings do no longer cause popup dialogs during configuration. The reason for this is to avoid the same warning to pop up many times. This would happen since each configuration change now causes a fresh reading of the file system - and thus each warning would be detected each time the configuration was changed. reltool_manual_gui_SUITE is updated to test the new functionality. --- lib/reltool/src/reltool_sys_win.erl | 207 +++++++++++++++++++++----- lib/reltool/src/reltool_utils.erl | 12 +- lib/reltool/test/reltool_manual_gui_SUITE.erl | 48 ++++-- 3 files changed, 215 insertions(+), 52 deletions(-) (limited to 'lib') diff --git a/lib/reltool/src/reltool_sys_win.erl b/lib/reltool/src/reltool_sys_win.erl index c75b302770..29a01b63d8 100644 --- a/lib/reltool/src/reltool_sys_win.erl +++ b/lib/reltool/src/reltool_sys_win.erl @@ -56,7 +56,9 @@ derived, fgraph_wins, app_box, - mod_box + mod_box, + warning_list, + warning_wins }). -define(WIN_WIDTH, 800). @@ -88,6 +90,10 @@ -define(blacklist, "Excluded"). -define(derived, "Derived"). +-define(WARNING_COL, 0). +-define(DEFAULT_WARNING_TIP, "Warnings are listed in this window"). +-define(WARNING_POPUP_SIZE, {400,150}). + -define(safe_config,{sys,[{incl_cond,exclude}, {app,kernel,[{incl_cond,include}]}, {app,stdlib,[{incl_cond,include}]}, @@ -150,7 +156,8 @@ do_init([{safe_config, Safe}, {parent, Parent} | Options]) -> target_dir = filename:absname("reltool_target_dir"), app_wins = [], sys = Sys, - fgraph_wins = []}, + fgraph_wins = [], + warning_wins = []}, S2 = create_window(S), S5 = wx:batch(fun() -> Title = atom_to_list(?APPLICATION), @@ -235,10 +242,18 @@ loop(S) -> lists:keydelete(ObjRef, #fgraph_win.frame, FWs), ?MODULE:loop(S#state{fgraph_wins = FWs2}); false -> - error_logger:format("~p~p got unexpected " - "message:\n\t~p\n", - [?MODULE, self(), Msg]), - ?MODULE:loop(S) + WWs = S#state.warning_wins, + case lists:member(ObjRef, WWs) of + true -> + wxFrame:destroy(ObjRef), + WWs2 = lists:delete(ObjRef, WWs), + ?MODULE:loop(S#state{warning_wins = WWs2}); + false -> + error_logger:format("~p~p got unexpected " + "message:\n\t~p\n", + [?MODULE, self(), Msg]), + ?MODULE:loop(S) + end end end; #wx{id = ?CLOSE_ITEM, @@ -292,8 +307,8 @@ handle_child_exit({'EXIT', Pid, _Reason} = Exit, FWs, AWs) -> msg_warning(Exit, application_window), {FWs, lists:keydelete(Pid, #app_win.pid, AWs)}; false -> - msg_warning(Exit, unknown), - {FWs, AWs} + msg_warning(Exit, unknown), + {FWs, AWs} end end. @@ -335,8 +350,12 @@ create_window(S) -> fun create_main_release_page/1, fun create_config_page/1 ]), + + S4 = create_warning_list(S3), + Sizer = wxBoxSizer:new(?wxVERTICAL), wxSizer:add(Sizer, Book, [{flag, ?wxEXPAND}, {proportion, 1}]), + wxSizer:add(Sizer, S4#state.warning_list, [{flag, ?wxEXPAND}]), wxPanel:setSizer(Panel, Sizer), wxSizer:fit(Sizer, Frame), @@ -344,7 +363,7 @@ create_window(S) -> wxFrame:connect(Frame, close_window), wxFrame:show(Frame), - S3. + S4. create_menubar(Frame) -> MenuBar = wxMenuBar:new(), @@ -637,6 +656,48 @@ redraw_config_page(#state{sys = Sys, app_box = AppBox, mod_box = ModBox} = S) -> wxRadioBox:setSelection(ModBox, ModChoice), S. +create_warning_list(#state{panel = Panel} = S) -> + ListCtrl = wxListCtrl:new(Panel, + [{style, + ?wxLC_REPORT bor + ?wxLC_HRULES bor + ?wxVSCROLL}, + {size, {?WIN_WIDTH,80}}]), + reltool_utils:assign_image_list(ListCtrl), + wxListCtrl:insertColumn(ListCtrl, ?WARNING_COL, "Warnings", + [{format,?wxLIST_FORMAT_LEFT}]), + wxListCtrl:setToolTip(ListCtrl, ?DEFAULT_WARNING_TIP), + wxEvtHandler:connect(ListCtrl, size, + [{skip, true}, {userData, warnings}]), + wxEvtHandler:connect(ListCtrl, command_list_item_activated, + [{userData, warnings}]), + wxEvtHandler:connect(ListCtrl, motion, [{userData, warnings}]), + wxEvtHandler:connect(ListCtrl, enter_window), + S#state{warning_list=ListCtrl}. + +redraw_warnings(S) -> + {ok,Warnings} = reltool_server:get_status(S#state.server_pid), + redraw_warnings(S#state.warning_list,Warnings), + length(Warnings). + +redraw_warnings(ListCtrl, []) -> + wxListCtrl:deleteAllItems(ListCtrl), + ok; +redraw_warnings(ListCtrl, Warnings) -> + wxListCtrl:deleteAllItems(ListCtrl), + Show = fun(Warning, Row) -> + wxListCtrl:insertItem(ListCtrl, Row, ""), + wxListCtrl:setItem(ListCtrl, + Row, + ?WARNING_COL, + Warning, + [{imageId, ?WARN_IMAGE}]), + Row + 1 + end, + wx:foldl(Show, 0, Warnings), + ok. + + create_main_release_page(#state{book = Book} = S) -> Panel = wxPanel:new(Book, []), RelBook = wxNotebook:new(Panel, ?wxID_ANY, []), @@ -778,6 +839,9 @@ escript_popup(S, File, Tree, Item) -> handle_event(S, #wx{id = Id, obj= ObjRef, userData = UserData, event = Event} = _Wx) -> %% io:format("wx: ~p\n", [Wx]), case Event of + _ when UserData =:= warnings; + is_tuple(UserData), element(1,UserData)=:=warning -> + handle_warning_event(S, ObjRef, UserData, Event); #wxSize{type = size, size = {W, _H}} when UserData =:= app_list_ctrl -> wxListCtrl:setColumnWidth(ObjRef, ?APPS_APP_COL, W), S; @@ -855,6 +919,78 @@ handle_event(S, #wx{id = Id, obj= ObjRef, userData = UserData, event = Event} = end end. +handle_warning_event(S, ObjRef, _, #wxSize{type = size}) -> + {Total, _} = wxWindow:getClientSize(ObjRef), + SBSize = scroll_size(ObjRef), + wxListCtrl:setColumnWidth(ObjRef, ?WARNING_COL, Total-SBSize), + S; +handle_warning_event(S, ObjRef, _, #wxMouse{type = motion, x=X, y=Y}) -> + Pos = reltool_utils:wait_for_stop_motion(ObjRef, {X,Y}), + Index = wxListCtrl:findItem(ObjRef,-1,Pos,0), + Tip = + case wxListCtrl:getItemText(ObjRef,Index) of + "" -> + ?DEFAULT_WARNING_TIP; + Text -> + "WARNING:\n" ++ Text + end, + wxListCtrl:setToolTip(ObjRef, Tip), + S; +handle_warning_event(S, ObjRef, _, #wxList{type = command_list_item_activated, + itemIndex = Pos}) -> + Text = wxListCtrl:getItemText(ObjRef, Pos), + S#state{warning_wins = [display_warning(S,Text) | S#state.warning_wins]}; +handle_warning_event(S, _ObjRef, {warning,Frame}, + #wxCommand{type = command_button_clicked}) -> + wxFrame:destroy(Frame), + S#state{warning_wins = lists:delete(Frame,S#state.warning_wins)}. + + +display_warning(S,Warning) -> + Pos = warning_popup_position(S,?WARNING_POPUP_SIZE), + Frame = wxFrame:new(wx:null(), ?wxID_ANY, "Warning",[{pos,Pos}]), + Panel = wxPanel:new(Frame, []), + TextStyle = ?wxTE_READONLY bor ?wxTE_WORDWRAP bor ?wxTE_MULTILINE, + Text = wxTextCtrl:new(Panel, ?wxID_ANY, [{value, Warning}, + {style, TextStyle}, + {size, ?WARNING_POPUP_SIZE}]), + Color = wxWindow:getBackgroundColour(Frame), + wxTextCtrl:setBackgroundColour(Text,Color), + Attr = wxTextAttr:new(), + wxTextAttr:setLeftIndent(Attr,10), + wxTextAttr:setRightIndent(Attr,10), + true = wxTextCtrl:setDefaultStyle(Text, Attr), + wxTextAttr:destroy(Attr), + Sizer = wxBoxSizer:new(?wxVERTICAL), + wxSizer:add(Sizer, Text, [{border, 2}, + {flag, ?wxEXPAND bor ?wxALL}, + {proportion, 1}]), + + Close = wxButton:new(Panel, ?wxID_ANY, [{label, "Close"}]), + wxButton:setToolTip(Close, "Close window."), + wxButton:connect(Close, command_button_clicked, [{userData,{warning,Frame}}]), + wxSizer:add(Sizer, Close, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]), + + wxPanel:setSizer(Panel, Sizer), + wxSizer:fit(Sizer, Frame), + wxSizer:setSizeHints(Sizer, Frame), + wxFrame:connect(Frame, close_window), + + wxFrame:show(Frame), + Frame. + +warning_popup_position(#state{frame=MF,warning_list=WL},{WFW,WFH}) -> + {MFX,MFY} = wxWindow:getPosition(MF), + {MFW,MFH} = wxWindow:getSize(MF), + {_WLW,WLH} = wxWindow:getSize(WL), + + %% Position the popup in the middle of the main frame, above the + %% warning list, and with a small offset from the exact middle... + Offset = 50, + X = MFX + (MFW-WFW) div 2 - Offset, + Y = MFY + (MFH-WLH-WFH) div 2 - Offset, + {X,Y}. + handle_popup_event(S, _Type, 0, _ObjRef, _UserData, _Str) -> S#state{popup_menu = undefined}; handle_popup_event(#state{popup_menu = #root_popup{dir = OldDir, @@ -1079,11 +1215,8 @@ handle_app_button(#state{server_pid = ServerPid, app_wins = AppWins} = S, Action) -> NewApps = [move_app(S, Item, Action) || Item <- Items], case reltool_server:set_apps(ServerPid, NewApps) of - {ok, []} -> + {ok, _Warnings} -> ok; - {ok, Warnings} -> - Msg = lists:flatten([[W, $\n] || W <- Warnings]), - display_message(Msg, ?wxICON_WARNING); {error, Reason} -> display_message(Reason, ?wxICON_ERROR) end, @@ -1122,21 +1255,17 @@ move_app(S, {_ItemNo, AppBase}, Action) -> do_set_app(#state{server_pid = ServerPid, app_wins = AppWins} = S, NewApp) -> Result = reltool_server:set_app(ServerPid, NewApp), - [ok = reltool_app_win:refresh(AW#app_win.pid) || AW <- AppWins], - S2 = redraw_apps(S), ReturnApp = case Result of - {ok, AnalysedApp, []} -> - AnalysedApp; - {ok, AnalysedApp, Warnings} -> - Msg = lists:flatten([[W, $\n] || W <- Warnings]), - display_message(Msg, ?wxICON_WARNING), + {ok, AnalysedApp, _Warnings} -> AnalysedApp; {error, Reason} -> display_message(Reason, ?wxICON_ERROR), {ok,OldApp} = reltool_server:get_app(ServerPid, NewApp#app.name), OldApp end, + [ok = reltool_app_win:refresh(AW#app_win.pid) || AW <- AppWins], + S2 = redraw_apps(S), {ok, ReturnApp, S2}. redraw_apps(#state{server_pid = ServerPid, @@ -1159,8 +1288,14 @@ redraw_apps(#state{server_pid = ServerPid, WhiteN = redraw_apps(WhiteApps, WhiteCtrl, ?TICK_IMAGE, ?ERR_IMAGE), redraw_apps(BlackApps2, BlackCtrl, ?CROSS_IMAGE, ?WARN_IMAGE), DerivedN = redraw_apps(DerivedApps, DerivedCtrl, ?TICK_IMAGE, ?ERR_IMAGE), + + WarningsN = redraw_warnings(S), + WarningText = if WarningsN==1 -> "warning"; + true -> "warnings" + end, Status = lists:concat([WhiteN, " whitelisted modules and ", - DerivedN, " derived modules."]), + DerivedN, " derived modules, ", + WarningsN, " ", WarningText, "."]), wxStatusBar:setStatusText(S#state.status_bar, Status), S. @@ -1375,8 +1510,8 @@ check_and_refresh(S, Status) -> case Status of ok -> true; - {ok, Warnings} -> - undo_dialog(S, Warnings); + {ok, _Warnings} -> + true; {error, Reason} when is_list(Reason) -> display_message(Reason, ?wxICON_ERROR), false; @@ -1430,19 +1565,6 @@ question_dialog(Question, Details) -> wxDialog:destroy(Dialog), Answer. -undo_dialog(_S, []) -> - true; -undo_dialog(S, Warnings) -> - Question = "Do you want to perform the update despite these warnings?", - Details = lists:flatten([[W, $\n] || W <- Warnings]), - case question_dialog(Question, Details) of - ?wxID_OK -> - true; - ?wxID_CANCEL -> - ok = reltool_server:undo_config(S#state.server_pid), - false - end. - display_message(Message, Icon) -> Dialog = wxMessageDialog:new(wx:null(), Message, @@ -1486,6 +1608,19 @@ add_text(_,_,[]) -> ok. +scroll_size(ObjRef) -> + case os:type() of + {win32, nt} -> 0; + {unix, darwin} -> + %% I can't figure out is there is a visible scrollbar + %% Always make room for it + wxSystemSettings:getMetric(?wxSYS_VSCROLL_X); + _ -> + case wxWindow:hasScrollbar(ObjRef, ?wxVERTICAL) of + true -> wxSystemSettings:getMetric(?wxSYS_VSCROLL_X); + false -> 0 + end + end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/lib/reltool/src/reltool_utils.erl b/lib/reltool/src/reltool_utils.erl index a9107355c7..2d766224d9 100644 --- a/lib/reltool/src/reltool_utils.erl +++ b/lib/reltool/src/reltool_utils.erl @@ -23,7 +23,7 @@ split_app_name/1, prim_consult/1, default_rels/0, choose_default/3, - assign_image_list/1, get_latest_resize/1, + assign_image_list/1, get_latest_resize/1, wait_for_stop_motion/2, mod_conds/0, list_to_mod_cond/1, mod_cond_to_index/1, incl_conds/0, list_to_incl_cond/1, incl_cond_to_index/1, elem_to_index/2, app_dir_test/2, split_app_dir/1, @@ -191,6 +191,16 @@ get_latest_resize(#wx{obj = ObjRef, event = #wxSize{}} = Wx) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +wait_for_stop_motion(ObjRef, {_,_}=Pos) -> + receive + #wx{obj = ObjRef, event = #wxMouse{type = motion, x=X, y=Y}} -> + wait_for_stop_motion(ObjRef, {X,Y}) + after 100 -> + Pos + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + mod_conds() -> ["all (ebin + app file)", "ebin + derived", "app file + derived", "derived", "none"]. diff --git a/lib/reltool/test/reltool_manual_gui_SUITE.erl b/lib/reltool/test/reltool_manual_gui_SUITE.erl index c6b1d56988..1ebee9fae1 100644 --- a/lib/reltool/test/reltool_manual_gui_SUITE.erl +++ b/lib/reltool/test/reltool_manual_gui_SUITE.erl @@ -67,37 +67,49 @@ config(Config) -> SimpleConfigFile = create_simple_config(PrivDir), WarningConfigFile = create_warning_config(PrivDir,DataDir), - break("there are no modules in the 'Included' and 'Excluded' columns", + break("there are no modules in the 'Included' and 'Excluded' columns, " + "and now warnings are displayed", {"load configuration ~p",[SimpleConfigFile]}), break("kernel, stdlib and sasl are included and all other are excluded", "undo"), break("we are back to default - no included and no excluded applications", "undo again"), break("kernel, stdlib and sasl are included and all other are excluded", - {"load configuration ~p, but click 'cancel' in the warning dialog", + {"load configuration ~p", [WarningConfigFile]}), - break("no change is done", - "load same configuration again and this time click 'ok' in the dialog"), - break("application a is added in the 'Included' column", + break("a warning is displayed in the warning list", + "undo"), + break("no warning is displayed in the warning list", + "load same configuration again"), + break("application a is added in the 'Included' column and " + "one warning is displayed", "reset configuration"), - break("we are back to default - no included and no excluded applications", + break("we are back to default - no included and no excluded applications, " + "and no warnings", "undo"), - break("a warning dialog is displayed, with only an ok button", - "click ok"), - break("a, kernel, stdlib and sasl are included and all other are excluded", + break("a, kernel, stdlib and sasl are included and all other are excluded. " + "One warning should now exist through the rest of this test case", + "double click the warning"), + break("a popup window occurs displaying the warning text", + "close it with the 'Close' button"), + break("it disappears", + "open it again"), + break("the warning text can be marked, copied and pasted", + "close the popup with the close box on the top frame"), + break("it disappears", "select application a from 'Included' column and click red cross to " "exclude it"), break("application a is moved to 'Excluded' column", "select application tools from 'Excluded' column and click green V to " "include it"), break("application tools is moved to 'Included' column", - "select application runtime-tools from 'Excluded' column and click " + "select application runtime_tools from 'Excluded' column and click " "green V to include it"), - break("application runtime-tools is moved to 'Included' column", + break("application runtime_tools is moved to 'Included' column", "undo"), ExplicitConfig = filename:join(PrivDir,"explicit.config"), - break("application runtime-tools is moved back to 'Excluded' column", + break("application runtime_tools is moved back to 'Excluded' column", {"save configuration as 'explicit' to ~p",[ExplicitConfig]}), ExpectedExplicitConfig = {sys,[{lib_dirs,[filename:join(DataDir,"faulty_app_file")]}, @@ -113,7 +125,7 @@ config(Config) -> "Now go to the 'Libraries' tab and change the root directory to " "some invalid directory."), break("an error dialog occurs saying that there is no lib dir", - {"add library directory ~p, and click 'ok' in warning dialog", + {"add library directory ~p", [filename:join(DataDir,"dependencies")]}), break("applications x, y and z are added to the 'Excluded' column in " "'Applications' tab", @@ -121,7 +133,7 @@ config(Config) -> "to 'derived'"), break("a is excluded, kernel, stdlib, sasl and tools are included and " "x, y and z are available in the 'Applications' tab", - "undo, and click ok in the warning dialog"), + "undo"), break("all non included application are moved to the 'Excluded' column " "and that 'Application inclusion policy' under 'System settings' " "tab is set back to 'exclude'", @@ -218,7 +230,13 @@ break(Check,Do) -> break(CheckStr,DoStr). break(Str) -> - test_server:break(Str). + Count = + case get(count) of + undefined -> 1; + C -> C + end, + put(count,Count+1), + test_server:break("Step " ++ integer_to_list(Count) ++ ":\n" ++ Str). check_config(Expected,File) -> {ok,[Config]} = file:consult(File), -- cgit v1.2.3 From 3f42375bbfc1657963f1d3aa7688f2ab67f31c1b Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 8 Mar 2012 11:03:02 +0100 Subject: [reltool] Move tables out of common record OTP-9794 This is a minor change, in order to keep tables private to reltool_server. --- lib/reltool/src/reltool.hrl | 7 +- lib/reltool/src/reltool_server.erl | 273 ++++++++++++++++++------------------- 2 files changed, 134 insertions(+), 146 deletions(-) (limited to 'lib') diff --git a/lib/reltool/src/reltool.hrl b/lib/reltool/src/reltool.hrl index 781c3f6d2c..d3eefb7ff0 100644 --- a/lib/reltool/src/reltool.hrl +++ b/lib/reltool/src/reltool.hrl @@ -127,12 +127,7 @@ { sys_debug :: term(), wx_debug :: term(), - trap_exit :: boolean(), - app_tab :: ets:tab(), - mod_tab :: ets:tab(), - mod_used_by_tab :: ets:tab(), - old_app_tab :: ets:tab(), - old_mod_tab :: ets:tab() + trap_exit :: boolean() }). -record(mod, diff --git a/lib/reltool/src/reltool_server.erl b/lib/reltool/src/reltool_server.erl index 4c22898559..90fc6a8c28 100644 --- a/lib/reltool/src/reltool_server.erl +++ b/lib/reltool/src/reltool_server.erl @@ -51,7 +51,12 @@ sys, old_sys, status, - old_status}). + old_status, + app_tab, + old_app_tab, + mod_tab, + old_mod_tab, + mod_used_by_tab}). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Client @@ -134,35 +139,34 @@ init([{parent,Parent}|_] = Options) -> end. do_init(Options) -> - AppTab = ets:new(reltool_apps, [public, ordered_set, {keypos, #app.name}]), - ModTab = ets:new(reltool_mods, [public, ordered_set, {keypos, #mod.name}]), - OldAppTab = ets:new(reltool_apps, [public, ordered_set, {keypos, #app.name}]), - OldModTab = ets:new(reltool_mods, [public, ordered_set, {keypos, #mod.name}]), + AppTab = ets:new(reltool_apps1, [public, ordered_set, {keypos,#app.name}]), + OldAppTab = ets:new(reltool_apps2, [public, ordered_set, {keypos,#app.name}]), + ModTab = ets:new(reltool_mods1, [public, ordered_set, {keypos,#mod.name}]), + OldModTab = ets:new(reltool_mods2, [public, ordered_set, {keypos,#mod.name}]), ModUsesTab = ets:new(reltool_mod_uses, [public, bag, {keypos, 1}]), - InitialC = #common{app_tab = AppTab, - mod_tab = ModTab, - old_app_tab = OldAppTab, - old_mod_tab = OldModTab, - mod_used_by_tab = ModUsesTab}, - - S = parse_options(InitialC, Options), - {S2, Apps, Status2} = refresh(S), - Status3 = analyse(S2, Apps, Status2), + S = #state{options = Options, + app_tab = AppTab, + old_app_tab = OldAppTab, + mod_tab = ModTab, + old_mod_tab = OldModTab, + mod_used_by_tab = ModUsesTab}, + + S2 = parse_options(S), + {S3, Apps, Status2} = refresh(S2), + Status3 = analyse(S3, Apps, Status2), %% Set old_xxx equal to xxx to allow undo=nop - FakeBackup = {ets:tab2list((S2#state.common)#common.app_tab), - ets:tab2list((S2#state.common)#common.mod_tab)}, - S3 = save_old(S2, S2, FakeBackup, Status3), - #state{parent_pid = Parent, sys=Sys, common=C} = S3, + FakeBackup = {ets:tab2list(S3#state.app_tab),ets:tab2list(S3#state.mod_tab)}, + S4 = save_old(S3, S3, FakeBackup, Status3), + #state{parent_pid = Parent, sys=Sys, common=C} = S4, proc_lib:init_ack(Parent, {ok, self(), C, Sys#sys{apps=undefined}}), - loop(S3). + loop(S4). -parse_options(C, Opts) -> +parse_options(S) -> Sys = default_sys(), - C2 = C#common{sys_debug = [], - wx_debug = 0, - trap_exit = true}, - S = #state{options = Opts}, - parse_options(Opts, S, C2, Sys). + C = #common{sys_debug = [], + wx_debug = 0, + trap_exit = true}, + parse_options(S#state.options, S, C, Sys). default_sys() -> #sys{root_dir = reltool_utils:root_dir(), @@ -227,14 +231,14 @@ parse_options([], S, C, Sys) -> parse_options(KeyVals, _S, _C, _Sys) -> reltool_utils:throw_error("Illegal option: ~p", [KeyVals]). -loop(#state{common = C, sys = Sys} = S) -> +loop(#state{sys = Sys} = S) -> receive {system, From, Msg} -> sys:handle_system_msg(Msg, From, S#state.parent_pid, ?MODULE, - C#common.sys_debug, + (S#state.common)#common.sys_debug, S); {call, ReplyTo, Ref, {get_config, InclDef, InclDeriv}} -> Reply = do_get_config(S, InclDef, InclDeriv), @@ -250,41 +254,38 @@ loop(#state{common = C, sys = Sys} = S) -> reltool_utils:reply(ReplyTo, Ref, Reply), ?MODULE:loop(S); {call, ReplyTo, Ref, reset_config} -> - Fun = fun() -> parse_options(C, S#state.options) end, + Fun = fun() -> parse_options(S) end, {S3, Status2} = config_and_refresh(S, Fun), reltool_utils:reply(ReplyTo, Ref, Status2), ?MODULE:loop(S3); {call, ReplyTo, Ref, undo_config} -> - C2 = C#common{app_tab = C#common.old_app_tab, - old_app_tab = C#common.app_tab, - mod_tab = C#common.old_mod_tab, - old_mod_tab = C#common.mod_tab}, - S2 = S#state{common = C2, - sys = S#state.old_sys, + S2 = S#state{sys = S#state.old_sys, old_sys = Sys, status = S#state.old_status, - old_status = S#state.status}, + old_status = S#state.status, + app_tab = S#state.old_app_tab, + old_app_tab = S#state.app_tab, + mod_tab = S#state.old_mod_tab, + old_mod_tab = S#state.mod_tab}, reltool_utils:reply(ReplyTo, Ref, ok), ?MODULE:loop(S2); {call, ReplyTo, Ref, {get_rel, RelName}} -> - Sys = S#state.sys, Reply = case lists:keysearch(RelName, #rel.name, Sys#sys.rels) of {value, Rel} -> - reltool_target:gen_rel(Rel, sys_all_apps(C,Sys)); + reltool_target:gen_rel(Rel, sys_all_apps(S)); false -> {error, "No such release: " ++ RelName} end, reltool_utils:reply(ReplyTo, Ref, Reply), ?MODULE:loop(S); {call, ReplyTo, Ref, {get_script, RelName}} -> - Sys = S#state.sys, Reply = case lists:keysearch(RelName, #rel.name, Sys#sys.rels) of {value, Rel} -> PathFlag = true, Vars = [], - reltool_target:gen_script(Rel, sys_all_apps(C,Sys), + reltool_target:gen_script(Rel, sys_all_apps(S), PathFlag, Vars); false -> {error, "No such release: " ++ RelName} @@ -293,7 +294,7 @@ loop(#state{common = C, sys = Sys} = S) -> ?MODULE:loop(S); {call, ReplyTo, Ref, {get_mod, ModName}} -> Reply = - case ets:lookup(C#common.mod_tab, ModName) of + case ets:lookup(S#state.mod_tab, ModName) of [M] -> {ok, M}; [] -> @@ -303,7 +304,7 @@ loop(#state{common = C, sys = Sys} = S) -> ?MODULE:loop(S); {call, ReplyTo, Ref, {get_app, AppName}} when is_atom(AppName) -> Reply = - case ets:lookup(C#common.app_tab,AppName) of + case ets:lookup(S#state.app_tab,AppName) of [App] -> {ok, App}; [] -> @@ -318,7 +319,7 @@ loop(#state{common = C, sys = Sys} = S) -> Reply = case Status2 of {ok, Warnings} -> - [App2] = ets:lookup(C#common.app_tab,App#app.name), + [App2] = ets:lookup(S3#state.app_tab,App#app.name), {ok, App2, Warnings}; {error, _} -> Status2 @@ -330,19 +331,19 @@ loop(#state{common = C, sys = Sys} = S) -> case Kind of whitelist -> %% Pre-included - ets:select(C#common.app_tab, + ets:select(S#state.app_tab, [{#app{is_pre_included=true,_='_'}, [], ['$_']}]); blacklist -> %% Pre-excluded - ets:select(C#common.app_tab, + ets:select(S#state.app_tab, [{#app{is_pre_included=false,_='_'}, [], ['$_']}]); source -> %% Not included and not pre-excluded - ets:select(C#common.app_tab, + ets:select(S#state.app_tab, [{#app{is_included='$1', is_pre_included='$2', _='_'}, @@ -351,7 +352,7 @@ loop(#state{common = C, sys = Sys} = S) -> ['$_']}]); derived -> %% Included, but not pre-included - ets:select(C#common.app_tab, + ets:select(S#state.app_tab, [{#app{is_included='$1', is_pre_included='$2', _='_'}, @@ -379,7 +380,7 @@ loop(#state{common = C, sys = Sys} = S) -> ?MODULE:loop(S); {call, ReplyTo, Ref, {gen_rel_files, Dir}} -> Status = - case reltool_target:gen_rel_files(sys_all_apps(C,Sys), Dir) of + case reltool_target:gen_rel_files(sys_all_apps(S), Dir) of ok -> {ok, []}; {error, Reason} -> @@ -388,11 +389,11 @@ loop(#state{common = C, sys = Sys} = S) -> reltool_utils:reply(ReplyTo, Ref, Status), ?MODULE:loop(S); {call, ReplyTo, Ref, {gen_target, Dir}} -> - Reply = reltool_target:gen_target(sys_all_apps(C,Sys), Dir), + Reply = reltool_target:gen_target(sys_all_apps(S), Dir), reltool_utils:reply(ReplyTo, Ref, Reply), ?MODULE:loop(S); {call, ReplyTo, Ref, gen_spec} -> - Reply = reltool_target:gen_spec(sys_all_apps(C,Sys)), + Reply = reltool_target:gen_spec(sys_all_apps(S)), reltool_utils:reply(ReplyTo, Ref, Reply), ?MODULE:loop(S); {'EXIT', Pid, Reason} when Pid =:= S#state.parent_pid -> @@ -510,7 +511,7 @@ mod_set_config_only(ConfigMods) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -analyse(#state{common=C, sys=Sys}, Apps, Status) -> +analyse(#state{sys=Sys} = S, Apps, Status) -> %% Create a list of {RelName,AppName}, one element for each %% AppName that needs to be included for the given release. RelApps = apps_in_rels(Sys#sys.rels, Apps), @@ -522,16 +523,16 @@ analyse(#state{common=C, sys=Sys}, Apps, Status) -> %% are no duplicated module names (in different applications) %% where we can not decide which one to use. %% Write all #app to app_tab and all #mod to mod_tab. - Status2 = apps_init_is_included(C, Sys, Apps, RelApps, Status), + Status2 = apps_init_is_included(S, Apps, RelApps, Status), %% For each module that has #mod.is_included==true, propagate %% is_included to the modules it uses. - propagate_is_included(C, Sys), + propagate_is_included(S), %% Insert reverse dependencies - i.e. for each %% #mod{name=Mod, uses_mods=[UsedMod]}, %% insert an entry {UsedMod,Mod} in mod_used_by_tab. - propagate_is_used_by(C), + propagate_is_used_by(S), %% Set the above reverse dependencies in #mod records %% (used_by_mods) and accumulate in #app records. @@ -539,13 +540,13 @@ analyse(#state{common=C, sys=Sys}, Apps, Status) -> %% #mod.is_included==true for at least one module in the app. %% Set status=missing|ok for #app and #mod - indicates if module %% (.beam file) is missing in file system. - app_recap_dependencies(C), + app_recap_dependencies(S), %% Check that the boot_rel exists. %% Check that all applications that are listed in a 'rel' spec are %% also really included in the target release. %% Check that all mandatory applications are included in all rels. - verify_config(C, Sys, RelApps, Status2). + verify_config(S, RelApps, Status2). apps_in_rels(Rels, Apps) -> AllRelApps = @@ -585,15 +586,14 @@ more_apps_in_rels([], _Apps, Acc) -> Acc. -apps_init_is_included(C, Sys, Apps, RelApps, Status) -> +apps_init_is_included(S, Apps, RelApps, Status) -> lists:foldl(fun(App, AccStatus) -> - app_init_is_included(C, Sys, App, RelApps, AccStatus) + app_init_is_included(S, App, RelApps, AccStatus) end, Status, Apps). -app_init_is_included(C, - Sys, +app_init_is_included(#state{app_tab = AppTab, mod_tab = ModTab, sys=Sys}, #app{name = AppName, mods = Mods} = A, RelApps, Status) -> @@ -624,7 +624,7 @@ app_init_is_included(C, {true, undefined, true, Status} end, {Mods2,Status3} = lists:mapfoldl(fun(Mod,Acc) -> - mod_init_is_included(C, + mod_init_is_included(ModTab, Mod, ModCond, AppCond, @@ -637,10 +637,10 @@ app_init_is_included(C, is_pre_included = IsPreIncl, is_included = IsIncl, rels = Rels}, - ets:insert(C#common.app_tab, A2), + ets:insert(AppTab, A2), Status3. -mod_init_is_included(C, M, ModCond, AppCond, Default, Status) -> +mod_init_is_included(ModTab, M, ModCond, AppCond, Default, Status) -> %% print(M#mod.name, hipe, "incl_cond -> ~p\n", [AppCond]), IsIncl = case AppCond of @@ -677,7 +677,7 @@ mod_init_is_included(C, M, ModCond, AppCond, Default, Status) -> M2 = M#mod{is_pre_included = IsIncl, is_included = IsIncl}, Status2 = - case ets:lookup(C#common.mod_tab,M#mod.name) of + case ets:lookup(ModTab,M#mod.name) of [Existing] -> case {Existing#mod.is_included,IsIncl} of {false,_} -> @@ -688,7 +688,7 @@ mod_init_is_included(C, M, ModCond, AppCond, Default, Status) -> " and ", M#mod.app_name, ". Using module from application ", M#mod.app_name, "."]), - ets:insert(C#common.mod_tab, M2), + ets:insert(ModTab, M2), reltool_utils:add_warning(Status,Warning); {_,false} -> Warning = @@ -699,7 +699,7 @@ mod_init_is_included(C, M, ModCond, AppCond, Default, Status) -> ". Using module from application ", Existing#mod.app_name, "."]), - %% Don't insert in mod_tab - using Existing + %% Don't insert in ModTab - using Existing reltool_utils:add_warning(Status,Warning); {_,_} -> reltool_utils:throw_error( @@ -708,7 +708,7 @@ mod_init_is_included(C, M, ModCond, AppCond, Default, Status) -> [M#mod.name,Existing#mod.app_name,M#mod.app_name]) end; [] -> - ets:insert(C#common.mod_tab, M2), + ets:insert(ModTab, M2), Status end, @@ -723,20 +723,20 @@ false_to_undefined(Bool) -> %% Return the list for {ModName, UsesModNames} for all modules where %% #mod.is_included==true. -get_all_mods_and_dependencies(C) -> - ets:select(C#common.mod_tab, [{#mod{name='$1', - uses_mods='$2', - is_included=true, - _='_'}, - [], - [{{'$1','$2'}}]}]). - -propagate_is_included(C, Sys) -> +get_all_mods_and_dependencies(S) -> + ets:select(S#state.mod_tab, [{#mod{name='$1', + uses_mods='$2', + is_included=true, + _='_'}, + [], + [{{'$1','$2'}}]}]). + +propagate_is_included(S) -> case lists:flatmap( fun({ModName,UsesModNames}) -> - mod_mark_is_included(C,Sys,ModName,UsesModNames,[]) + mod_mark_is_included(S,ModName,UsesModNames,[]) end, - get_all_mods_and_dependencies(C)) of + get_all_mods_and_dependencies(S)) of [] -> ok; MissingMods -> @@ -746,13 +746,14 @@ propagate_is_included(C, Sys) -> mods = MissingMods, status = missing, uses_mods = []}, - ets:insert(C#common.app_tab, MissingApp2), + ets:insert(S#state.app_tab, MissingApp2), ok end. -mod_mark_is_included(C, Sys, UsedByName, [ModName | ModNames], Acc) -> +mod_mark_is_included(#state{app_tab=AppTab, mod_tab=ModTab, sys=Sys} = S, + UsedByName, [ModName | ModNames], Acc) -> Acc3 = - case ets:lookup(C#common.mod_tab, ModName) of + case ets:lookup(ModTab, ModName) of [M] -> case M#mod.is_included of undefined -> @@ -768,8 +769,8 @@ mod_mark_is_included(C, Sys, UsedByName, [ModName | ModNames], Acc) -> undefined -> M#mod{is_included = true} end, - ets:insert(C#common.mod_tab, M2), - [A] = ets:lookup(C#common.app_tab, M2#mod.app_name), + ets:insert(ModTab, M2), + [A] = ets:lookup(AppTab, M2#mod.app_name), Acc2 = case A#app.is_included of undefined -> @@ -790,9 +791,8 @@ mod_mark_is_included(C, Sys, UsedByName, [ModName | ModNames], Acc) -> end, Mods = lists:filter(Filter, A#app.mods), A2 = A#app{is_included = true}, - ets:insert(C#common.app_tab, A2), - mod_mark_is_included(C, - Sys, + ets:insert(AppTab, A2), + mod_mark_is_included(S, ModName, [M3#mod.name || M3 <- Mods], @@ -801,11 +801,7 @@ mod_mark_is_included(C, Sys, UsedByName, [ModName | ModNames], Acc) -> %% Already marked true or false Acc end, - mod_mark_is_included(C, - Sys, - ModName, - M2#mod.uses_mods, - Acc2); + mod_mark_is_included(S, ModName, M2#mod.uses_mods, Acc2); _ -> %% Already marked true or false Acc @@ -813,31 +809,31 @@ mod_mark_is_included(C, Sys, UsedByName, [ModName | ModNames], Acc) -> [] -> M = missing_mod(ModName, ?MISSING_APP_NAME), M2 = M#mod{is_included = true}, - ets:insert(C#common.mod_tab, M2), + ets:insert(ModTab, M2), [M2 | Acc] end, - mod_mark_is_included(C, Sys, UsedByName, ModNames, Acc3); -mod_mark_is_included(_C, _Sys, _UsedByName, [], Acc) -> + mod_mark_is_included(S, UsedByName, ModNames, Acc3); +mod_mark_is_included(_S, _UsedByName, [], Acc) -> Acc. -propagate_is_used_by(C) -> +propagate_is_used_by(S) -> lists:foreach( fun({Mod,UsesMods}) -> lists:foreach( fun(UsedMod) -> - ets:insert(C#common.mod_used_by_tab,{UsedMod,Mod}) + ets:insert(S#state.mod_used_by_tab,{UsedMod,Mod}) end, UsesMods) end, - get_all_mods_and_dependencies(C)). + get_all_mods_and_dependencies(S)). -app_recap_dependencies(C) -> - ets:foldl(fun(App,_) -> app_recap_dependencies(C,App) end, - ok, C#common.app_tab). +app_recap_dependencies(S) -> + ets:foldl(fun(App,_) -> app_recap_dependencies(S,App) end, + ok, S#state.app_tab). -app_recap_dependencies(C, #app{mods = Mods, is_included = IsIncl} = A) -> - {Mods2, IsIncl2} = mod_recap_dependencies(C, A, Mods, [], IsIncl), +app_recap_dependencies(S, #app{mods = Mods, is_included = IsIncl} = A) -> + {Mods2, IsIncl2} = mod_recap_dependencies(S, A, Mods, [], IsIncl), AppStatus = case lists:keymember(missing, #mod.status, Mods2) of true -> missing; @@ -846,12 +842,12 @@ app_recap_dependencies(C, #app{mods = Mods, is_included = IsIncl} = A) -> UsesMods = [M#mod.uses_mods || M <- Mods2, M#mod.is_included =:= true], UsesMods2 = lists:usort(lists:flatten(UsesMods)), UsesApps = [M#mod.app_name || ModName <- UsesMods2, - M <- ets:lookup(C#common.mod_tab, ModName)], + M <- ets:lookup(S#state.mod_tab, ModName)], UsesApps2 = lists:usort(UsesApps), UsedByMods = [M#mod.used_by_mods || M <- Mods2, M#mod.is_included =:= true], UsedByMods2 = lists:usort(lists:flatten(UsedByMods)), UsedByApps = [M#mod.app_name || ModName <- UsedByMods2, - M <- ets:lookup(C#common.mod_tab, ModName)], + M <- ets:lookup(S#state.mod_tab, ModName)], UsedByApps2 = lists:usort(UsedByApps), A2 = A#app{mods = Mods2, @@ -861,11 +857,11 @@ app_recap_dependencies(C, #app{mods = Mods, is_included = IsIncl} = A) -> uses_apps = UsesApps2, used_by_apps = UsedByApps2, is_included = IsIncl2}, - ets:insert(C#common.app_tab,A2), + ets:insert(S#state.app_tab,A2), ok. -mod_recap_dependencies(C, A, [#mod{name = ModName}=M1 | Mods], Acc, IsIncl) -> - case ets:lookup(C#common.mod_tab, ModName) of +mod_recap_dependencies(S, A, [#mod{name = ModName}=M1 | Mods], Acc, IsIncl) -> + case ets:lookup(S#state.mod_tab, ModName) of [M2] when M2#mod.app_name=:=A#app.name -> ModStatus = do_get_status(M2), %% print(M2#mod.name, hipe, "status -> ~p\n", [ModStatus]), @@ -873,19 +869,19 @@ mod_recap_dependencies(C, A, [#mod{name = ModName}=M1 | Mods], Acc, IsIncl) -> case M2#mod.is_included of true -> UsedByMods = - [N || {_, N} <- ets:lookup(C#common.mod_used_by_tab, + [N || {_, N} <- ets:lookup(S#state.mod_used_by_tab, ModName)], {true, M2#mod{status = ModStatus, used_by_mods = UsedByMods}}; _ -> {IsIncl, M2#mod{status = ModStatus, used_by_mods = []}} end, - ets:insert(C#common.mod_tab, M3), - mod_recap_dependencies(C, A, Mods, [M3 | Acc], IsIncl2); + ets:insert(S#state.mod_tab, M3), + mod_recap_dependencies(S, A, Mods, [M3 | Acc], IsIncl2); [_] when A#app.is_included==false; M1#mod.incl_cond==exclude -> %% App is explicitely excluded so it is ok that the module %% record does not exist for this module in this %% application. - mod_recap_dependencies(C, A, Mods, [M1 | Acc], IsIncl); + mod_recap_dependencies(S, A, Mods, [M1 | Acc], IsIncl); [M2] -> %% A module is potensially included by multiple %% applications. This is not allowed! @@ -893,7 +889,7 @@ mod_recap_dependencies(C, A, [#mod{name = ModName}=M1 | Mods], Acc, IsIncl) -> "Module ~p potentially included by two different applications: " "~p and ~p", [ModName,A#app.name, " and ", M2#mod.app_name, "."]) end; -mod_recap_dependencies(_C, _A, [], Acc, IsIncl) -> +mod_recap_dependencies(_S, _A, [], Acc, IsIncl) -> {lists:reverse(Acc), IsIncl}. do_get_status(M) -> @@ -904,11 +900,12 @@ do_get_status(M) -> ok end. -verify_config(C, #sys{boot_rel = BootRel, rels = Rels}, RelApps, Status) -> +verify_config(#state{app_tab=AppTab, sys=#sys{boot_rel = BootRel, rels = Rels}}, + RelApps, Status) -> case lists:keymember(BootRel, #rel.name, Rels) of true -> Status2 = lists:foldl(fun(RA, Acc) -> - check_app(C, RA, Acc) end, + check_app(AppTab, RA, Acc) end, Status, RelApps), lists:foldl(fun(#rel{name = RelName}, Acc)-> @@ -921,8 +918,8 @@ verify_config(C, #sys{boot_rel = BootRel, rels = Rels}, RelApps, Status) -> "Release ~p is mandatory (used as boot_rel)",[BootRel]) end. -check_app(C, {RelName, AppName}, Status) -> - case ets:lookup(C#common.app_tab, AppName) of +check_app(AppTab, {RelName, AppName}, Status) -> + case ets:lookup(AppTab, AppName) of [#app{is_pre_included=IsPreIncl, is_included=IsIncl}] when IsPreIncl; IsIncl -> Status; @@ -1001,7 +998,7 @@ refresh_app(#app{name = AppName, %% Add optional user config for each module. %% The #mod records that are already in the #app record at %% this point do only contain user defined configuration - %% (set by parse_options/2). So here we merge with the + %% (set by parse_options/4). So here we merge with the %% default records from above. Mods2 = add_mod_config(MissingMods ++ EbinMods, Mods), @@ -1221,7 +1218,7 @@ set_mod_flags(Mods, AppModNames) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% do_get_config(S, InclDef, InclDeriv) -> - AppTab = (S#state.common)#common.app_tab, + AppTab = S#state.app_tab, Sys = case InclDeriv of false -> @@ -1232,7 +1229,7 @@ do_get_config(S, InclDef, InclDeriv) -> App <- ets:lookup(AppTab,Name)], (S#state.sys)#sys{apps=Apps}; true -> - sys_all_apps(S#state.common,S#state.sys) + sys_all_apps(S) end, reltool_target:gen_config(Sys, InclDef). @@ -1940,11 +1937,8 @@ get_base(Name, Dir) -> filename:basename(Dir) end. -sys_all_apps(C,Sys) -> - Sys#sys{apps = all_apps(C)}. - -all_apps(C) -> - ets:match_object(C#common.app_tab,'_'). +sys_all_apps(#state{app_tab=AppTab, sys=Sys}) -> + Sys#sys{apps = ets:match_object(AppTab,'_')}. config_and_refresh(OldS, Fun) -> try @@ -1966,27 +1960,26 @@ config_and_refresh(OldS, Fun) -> end. -backup(#state{common=C}) -> - Apps = ets:tab2list(C#common.app_tab), - Mods = ets:tab2list(C#common.mod_tab), - ets:delete_all_objects(C#common.app_tab), - ets:delete_all_objects(C#common.mod_tab), - ets:delete_all_objects(C#common.mod_used_by_tab), %tmp tab, no backup needed +backup(S) -> + Apps = ets:tab2list(S#state.app_tab), + Mods = ets:tab2list(S#state.mod_tab), + ets:delete_all_objects(S#state.app_tab), + ets:delete_all_objects(S#state.mod_tab), + ets:delete_all_objects(S#state.mod_used_by_tab), %tmp tab, no backup needed {Apps,Mods}. -restore({Apps,Mods}, #state{common=C}) -> - insert_all(C#common.app_tab,Apps), - insert_all(C#common.mod_tab,Mods). +restore({Apps,Mods}, S) -> + insert_all(S#state.app_tab,Apps), + insert_all(S#state.mod_tab,Mods). -save_old(#state{status=OldStatus,sys=OldSys},#state{common=C}=NewS, - {OldApps,OldMods},NewStatus) -> - ets:delete_all_objects(C#common.old_app_tab), - ets:delete_all_objects(C#common.old_mod_tab), - insert_all(C#common.old_app_tab,OldApps), - insert_all(C#common.old_mod_tab,OldMods), +save_old(#state{status=OldStatus,sys=OldSys},NewS,{OldApps,OldMods},NewStatus) -> + ets:delete_all_objects(NewS#state.old_app_tab), + ets:delete_all_objects(NewS#state.old_mod_tab), + insert_all(NewS#state.old_app_tab,OldApps), + insert_all(NewS#state.old_mod_tab,OldMods), NewS#state{old_sys=OldSys, - old_status=OldStatus, - status=NewStatus}. + old_status=OldStatus, + status=NewStatus}. insert_all(Tab,Items) -> lists:foreach(fun(Item) -> ets:insert(Tab,Item) end, Items). -- cgit v1.2.3 From 048c28bc6c75964c3ee7930f5a1aecae92843c88 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 12 Mar 2012 12:35:05 +0100 Subject: [reltool] Fix type spec and doc for app() and mod() OTP-9792 Also correct documentation of reltool:install/2: first argument is RelName, not Server. --- lib/reltool/doc/src/reltool.xml | 11 +++++------ lib/reltool/src/reltool.hrl | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/reltool/doc/src/reltool.xml b/lib/reltool/doc/src/reltool.xml index 60e886e8f5..696ab52e63 100644 --- a/lib/reltool/doc/src/reltool.xml +++ b/lib/reltool/doc/src/reltool.xml @@ -5,7 +5,7 @@
2009 - 2011 + 2012 Ericsson AB, All Rights Reserved @@ -433,7 +433,7 @@ sys() = {root_dir, root_dir()} | {excl_archive_filters, excl_archive_filters()} | {archive_opts, [archive_opt()]} app() = {vsn, app_vsn()} - | {mod, mod_name(), mod()} + | {mod, mod_name(), [mod()]} | {mod_cond, mod_cond()} | {incl_cond, incl_cond()} | {debug_info, debug_info()} @@ -445,8 +445,7 @@ app() = {vsn, app_vsn()} | {incl_archive_filters, incl_archive_filters()} | {excl_archive_filters, excl_archive_filters()} | {archive_opts, [archive_opt()]} -mod() = {vsn, app_vsn()} - | {incl_cond, incl_cond()} +mod() = {incl_cond, incl_cond()} | {debug_info, debug_info()} rel_app() = app_name() | {app_name(), app_type()} @@ -664,10 +663,10 @@ target_spec() = [target_spec()] - install(Server, TargetDir) -> ok | {error, Reason} + install(RelName, TargetDir) -> ok | {error, Reason} Install a target system - Server = server() + RelName = rel_name() TargetDir = target_dir() Reason = reason() diff --git a/lib/reltool/src/reltool.hrl b/lib/reltool/src/reltool.hrl index d3eefb7ff0..fc0078715a 100644 --- a/lib/reltool/src/reltool.hrl +++ b/lib/reltool/src/reltool.hrl @@ -63,7 +63,7 @@ -type mod() :: {incl_cond, incl_cond()} | {debug_info, debug_info()}. -type app() :: {vsn, app_vsn()} - | {mod, mod_name(), mod()} + | {mod, mod_name(), [mod()]} | {mod_cond, mod_cond()} | {incl_cond, incl_cond()} | {app_file, app_file()} -- cgit v1.2.3 From 50bc03bf5cc4b55644d164819d8c58bef966e278 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 12 Mar 2012 17:21:33 +0100 Subject: [reltool] Always return warnings as flat strings OTP-9794 --- lib/reltool/src/reltool_server.erl | 63 ++++++++++++++++++-------------------- lib/reltool/src/reltool_utils.erl | 5 +-- 2 files changed, 32 insertions(+), 36 deletions(-) (limited to 'lib') diff --git a/lib/reltool/src/reltool_server.erl b/lib/reltool/src/reltool_server.erl index 90fc6a8c28..d43be82cbd 100644 --- a/lib/reltool/src/reltool_server.erl +++ b/lib/reltool/src/reltool_server.erl @@ -681,26 +681,21 @@ mod_init_is_included(ModTab, M, ModCond, AppCond, Default, Status) -> [Existing] -> case {Existing#mod.is_included,IsIncl} of {false,_} -> - Warning = - lists:concat( - ["Module ",M#mod.name, - " exists in applications ", Existing#mod.app_name, - " and ", M#mod.app_name, - ". Using module from application ", - M#mod.app_name, "."]), ets:insert(ModTab, M2), - reltool_utils:add_warning(Status,Warning); + reltool_utils:add_warning( + "Module ~p exists in applications ~p and ~p. " + "Using module from application ~p.", + [M#mod.name, Existing#mod.app_name, + M#mod.app_name, M#mod.app_name], + Status); {_,false} -> - Warning = - lists:concat( - ["Module ",M#mod.name, - " exists in applications ", Existing#mod.app_name, - " and ", M#mod.app_name, - ". Using module from application ", - Existing#mod.app_name, "."]), - %% Don't insert in ModTab - using Existing - reltool_utils:add_warning(Status,Warning); + reltool_utils:add_warning( + "Module ~p exists in applications ~p and ~p. " + "Using module from application ~p.", + [M#mod.name, Existing#mod.app_name, + M#mod.app_name,Existing#mod.app_name], + Status); {_,_} -> reltool_utils:throw_error( "Module ~p potentially included by two different " @@ -1035,22 +1030,21 @@ read_app_info(AppFileOrBin, AppFile, AppName, DefaultVsn, Status) -> AI = #app_info{vsn = DefaultVsn}, parse_app_info(AppFile, Info, AI, Status); {ok, _BadApp} -> - Text = lists:concat([AppName, - ": Illegal contents in app file ", AppFile, - ", application tuple with arity 3 expected."]), {missing_app_info(DefaultVsn), - reltool_utils:add_warning(Status, Text)}; + reltool_utils:add_warning("~p: Illegal contents in app file ~p, " + "application tuple with arity 3 expected.", + [AppName,AppFile], + Status)}; {error, Text} when Text =:= EnoentText -> - Text2 = lists:concat([AppName, - ": Missing app file ", AppFile, "."]), {missing_app_info(DefaultVsn), - reltool_utils:add_warning(Status, Text2)}; + reltool_utils:add_warning("~p: Missing app file ~p.", + [AppName,AppFile], + Status)}; {error, Text} -> - Text2 = lists:concat([AppName, - ": Cannot parse app file ", - AppFile, " (", Text, ")."]), {missing_app_info(DefaultVsn), - reltool_utils:add_warning(Status, Text2)} + reltool_utils:add_warning("~p: Cannot parse app file ~p (~p).", + [AppName,AppFile,Text], + Status)} end. parse_app_info(File, [{Key, Val} | KeyVals], AI, Status) -> @@ -1084,10 +1078,11 @@ parse_app_info(File, [{Key, Val} | KeyVals], AI, Status) -> parse_app_info(File, KeyVals, AI#app_info{start_phases = Val}, Status); _ -> - String = lists:concat(["Unexpected item ", - Key, "in app file ", File]), - parse_app_info(File, KeyVals, AI, - reltool_utils:add_warning(Status, String)) + Status2 = + reltool_utils:add_warning("Unexpected item ~p in app file ~p.", + [Key,File], + Status), + parse_app_info(File, KeyVals, AI, Status2) end; parse_app_info(_, [], AI, Status) -> {AI, Status}. @@ -1581,8 +1576,8 @@ patch_erts_version(RootDir, Apps, Status) -> Apps2 = lists:keystore(AppName, #app.name, Apps, Erts2), {Apps2, Status}; Vsn =:= "" -> - {Apps, reltool_utils:add_warning(Status, - "erts has no version")}; + {Apps, reltool_utils:add_warning("erts has no version",[], + Status)}; true -> {Apps, Status} end; diff --git a/lib/reltool/src/reltool_utils.erl b/lib/reltool/src/reltool_utils.erl index 2d766224d9..9cf9bd1418 100644 --- a/lib/reltool/src/reltool_utils.erl +++ b/lib/reltool/src/reltool_utils.erl @@ -30,7 +30,7 @@ get_item/1, get_items/1, get_selected_items/3, select_items/3, select_item/2, - safe_keysearch/5, print/4, add_warning/2, + safe_keysearch/5, print/4, add_warning/3, create_dir/1, list_dir/1, read_file_info/1, write_file_info/2, read_file/1, write_file/2, @@ -402,7 +402,8 @@ print(X, X, Format, Args) -> print(_, _, _, _) -> ok. -add_warning({ok,Warnings}, Warning) -> +add_warning(Format, Args, {ok,Warnings}) -> + Warning = lists:flatten(io_lib:format(Format,Args)), case lists:member(Warning,Warnings) of true -> {ok,Warnings}; -- cgit v1.2.3 From dac94945228322ffb9f8dd8495789c0cd64442a3 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 12 Mar 2012 17:34:15 +0100 Subject: [reltool] Just warn if same module occurs twice in .app file OTP-9792 Earlier this would cause an error with reason "Module xxx potentially included by two different applications: yyy and yyy." This is now changed so it will only be a warning saying that the module is duplicated in the .app file. --- lib/reltool/src/reltool_server.erl | 40 +++++++++++++++------- lib/reltool/test/reltool_server_SUITE.erl | 40 +++++++++++++++++++--- .../dupl_mod/a-1.0/ebin/a.app | 7 ++++ 3 files changed, 70 insertions(+), 17 deletions(-) create mode 100644 lib/reltool/test/reltool_server_SUITE_data/dupl_mod/a-1.0/ebin/a.app (limited to 'lib') diff --git a/lib/reltool/src/reltool_server.erl b/lib/reltool/src/reltool_server.erl index d43be82cbd..29df619955 100644 --- a/lib/reltool/src/reltool_server.erl +++ b/lib/reltool/src/reltool_server.erl @@ -977,18 +977,32 @@ refresh_app(#app{name = AppName, %% Add non-existing modules - i.e. create default #mod %% records for all modules that are listed in .app file %% but do not exist in ebin. - AppInfoMods = AppInfo#app_info.modules, - AppModNames = - case AppInfo#app_info.mod of - {StartModName, _} -> - case lists:member(StartModName, AppInfoMods) of - true -> AppInfoMods; - false -> [StartModName | AppInfoMods] - end; - undefined -> - AppInfoMods - end, - MissingMods = add_missing_mods(AppName, EbinMods, AppModNames), + AppInfoMods = lists:usort(AppInfo#app_info.modules), + Status4 = + case AppInfo#app_info.modules -- AppInfoMods of + [] -> + Status3; + DuplicatedMods -> + lists:foldl( + fun(M,S) -> + reltool_utils:add_warning( + "Module ~p duplicated in app file for " + "application ~p.", [M, AppName], S) + end, + Status3, + DuplicatedMods) + end, + AppModNames = + case AppInfo#app_info.mod of + {StartModName, _} -> + case lists:member(StartModName, AppInfoMods) of + true -> AppInfoMods; + false -> [StartModName | AppInfoMods] + end; + undefined -> + AppInfoMods + end, + MissingMods = add_missing_mods(AppName, EbinMods, AppModNames), %% Add optional user config for each module. %% The #mod records that are already in the #app record at @@ -1013,7 +1027,7 @@ refresh_app(#app{name = AppName, label = AppLabel, info = AppInfo, mods = lists:keysort(#mod.name, Mods3)}, - {App2, Status3}; + {App2, Status4}; true -> {App, Status} end. diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl index 4b0511afaf..57b04251d4 100644 --- a/lib/reltool/test/reltool_server_SUITE.erl +++ b/lib/reltool/test/reltool_server_SUITE.erl @@ -69,8 +69,9 @@ all() -> create_old_target, eval_target_spec, otp_9135, - otp_9229_exclude_app, - otp_9229_exclude_mod, + otp_9229_dupl_mod_exclude_app, + otp_9229_dupl_mod_exclude_mod, + dupl_mod_in_app_file, get_apps, get_mod, get_sys, @@ -1002,7 +1003,7 @@ eval_target_spec(_Config) -> %% exists in two applications. %% Include on app, exclude the other -otp_9229_exclude_app(Config) -> +otp_9229_dupl_mod_exclude_app(Config) -> DataDir = ?config(data_dir,Config), LibDir = filename:join(DataDir,"otp_9229"), @@ -1049,7 +1050,7 @@ otp_9229_exclude_app(Config) -> ok. %% Include both apps, but exclude common module from one app -otp_9229_exclude_mod(Config) -> +otp_9229_dupl_mod_exclude_mod(Config) -> DataDir = ?config(data_dir,Config), LibDir = filename:join(DataDir,"otp_9229"), @@ -1102,6 +1103,37 @@ otp_9229_exclude_mod(Config) -> ok. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Test that if a module is duplicated in a .app file, then a warning +%% is produced, but target can still be created. +dupl_mod_in_app_file(Config) -> + DataDir = ?config(data_dir,Config), + LibDir = filename:join(DataDir,"dupl_mod"), + + %% Configure the server + Sys = + {sys, + [ + {lib_dirs, [LibDir]}, + {incl_cond,exclude}, + {app,a,[{incl_cond,include}]}, + {app,kernel,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]} + ]}, + + %% Generate target file + TargetDir = filename:join([?WORK_DIR, "target_dupl_mod_in_app_file"]), + ?m(ok, reltool_utils:recursive_delete(TargetDir)), + ?m(ok, file:make_dir(TargetDir)), + ?log("SPEC: ~p\n", [reltool:get_target_spec([{config, Sys}])]), + ?m({ok,["Module a duplicated in app file for application a."]}, + reltool:get_status([{config, Sys}])), + + %%! test that only one module installed (in spec) + + ok. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Test the interface used by the GUI: %% get_app diff --git a/lib/reltool/test/reltool_server_SUITE_data/dupl_mod/a-1.0/ebin/a.app b/lib/reltool/test/reltool_server_SUITE_data/dupl_mod/a-1.0/ebin/a.app new file mode 100644 index 0000000000..fada34847a --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/dupl_mod/a-1.0/ebin/a.app @@ -0,0 +1,7 @@ +% -*-erlang-*- +{application, a, + [{description, "Application with duplicated module name in .app file"}, + {vsn, "1.0"}, + {modules, [a,a]}, + {registered, []}, + {applications, [kernel, stdlib]}]}. -- cgit v1.2.3 From ba3e53ef71cc127d4df9c53f9f401645814e3fb7 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 15 Mar 2012 11:52:41 +0100 Subject: [reltool] Fix problems with sorting of applications OTP-9792 The following problems have been solved: * reltool_target:do_merge_apps - in recursive calls to this function, the accumulator was reverted each time causing the order of applications listed after kernel and stdlib in the rel specification in the configuration to sometimes be messed up. * There are several ways to specify wich applications to include in an application: 1) in the .app file for the including applications 2a) in the .rel file, when listing applications 2b) in the rel specification in the reltool configuration 2a (systools) and 2b (reltool) should have the same effect and overwrite 1. According to the documentation of systools (sasl), the default value in 2a is an empty list. This should mean that if included applications are not mentioned in the .rel file, then any included application listed in the .app file will be disregarded. This is NOT the way systools actually works. The implementation sets the default for the .rel file to the same list as in the .app file. Reltool earlier implemented 2b as described in the systools documentation. However, after some discussion we decided to change this so that reltool handles 2b in the same way as systools handles 2a since this seems more intuitive. The sasl documentation will be altered accordingly (internal ref OTP-9980). * If the rel specification in the reltool configuration explicitly specified included applications to be an empty list, and the .app file had a non-empty list, then the empty list from the rel specification was discarded. This has been corrected so the rel specification now, if set, always overwrites the value of included_applications in the .app file. * reltool would earlier add load instructions in the script/boot files for ALL modules in the ebin directory of an application even if mod_cond was set to app (include only modules listed in the .app file). This has been corrected - now only modules with #mod.is_included==true are loaded. * reltool would earlier add start instructions in the script/boot file for included applications. This has been corrected - included applications shall only be loaded since the including application is responsible for starting them. --- lib/reltool/src/reltool.hrl | 2 +- lib/reltool/src/reltool_server.erl | 30 +++++-- lib/reltool/src/reltool_target.erl | 72 +++++++++------- lib/reltool/src/reltool_utils.erl | 8 +- lib/reltool/test/reltool_server_SUITE.erl | 95 ++++++++++++---------- .../sort_apps/z-1.0/ebin/z.app | 4 +- 6 files changed, 121 insertions(+), 90 deletions(-) (limited to 'lib') diff --git a/lib/reltool/src/reltool.hrl b/lib/reltool/src/reltool.hrl index fc0078715a..4e5c5b2849 100644 --- a/lib/reltool/src/reltool.hrl +++ b/lib/reltool/src/reltool.hrl @@ -206,7 +206,7 @@ { name :: app_name(), app_type :: app_type() | undefined, - incl_apps = [] :: [incl_app()] + incl_apps :: [incl_app()] | undefined }). -record(rel, diff --git a/lib/reltool/src/reltool_server.erl b/lib/reltool/src/reltool_server.erl index 29df619955..c99180a613 100644 --- a/lib/reltool/src/reltool_server.erl +++ b/lib/reltool/src/reltool_server.erl @@ -560,10 +560,30 @@ apps_in_rels(Rels, Apps) -> apps_in_rel(#rel{name = RelName, rel_apps = RelApps}, Apps) -> Mandatory = [{RelName, kernel}, {RelName, stdlib}], - Other = [{RelName, AppName} || - RA <- RelApps, - AppName <- [RA#rel_app.name | RA#rel_app.incl_apps], - not lists:keymember(AppName, 2, Mandatory)], + Other = + [{RelName, AppName} || + RA <- RelApps, + AppName <- [RA#rel_app.name | + %% Included applications in rel shall overwrite included + %% applications in .app. I.e. included applications in + %% .app shall only be used if it is not defined in rel. + case RA#rel_app.incl_apps of + undefined -> + case lists:keyfind(RA#rel_app.name, + #app.name, + Apps) of + #app{info = #app_info{incl_apps = IA}} -> + IA; + false -> + reltool_utils:throw_error( + "Release ~p uses non existing " + "application ~p", + [RelName,RA#rel_app.name]) + end; + IA -> + IA + end], + not lists:keymember(AppName, 2, Mandatory)], more_apps_in_rels(Mandatory ++ Other, Apps, []). more_apps_in_rels([{RelName, AppName} = RA | RelApps], Apps, Acc) -> @@ -1505,7 +1525,7 @@ decode(#rel{rel_apps = RelApps} = Rel, [RelApp | KeyVals]) -> {VT andalso VI, #rel_app{name = Name, app_type = Type, incl_apps = InclApps}}; _ -> - {false, #rel_app{incl_apps = []}} + {false, #rel_app{}} end, case ValidTypesAssigned of true -> diff --git a/lib/reltool/src/reltool_target.erl b/lib/reltool/src/reltool_target.erl index 40d1009733..3d83a77d99 100644 --- a/lib/reltool/src/reltool_target.erl +++ b/lib/reltool/src/reltool_target.erl @@ -208,10 +208,10 @@ do_gen_config(#rel_app{name = Name, incl_apps = InclApps}, _InclDefs) -> case {Type, InclApps} of - {undefined, []} -> Name; - {undefined, _} -> {Name, InclApps}; - {_, []} -> {Name, Type}; - {_, _} -> {Name, Type, InclApps} + {undefined, undefined} -> Name; + {undefined, _} -> {Name, InclApps}; + {_, undefined} -> {Name, Type}; + {_, _} -> {Name, Type, InclApps} end; do_gen_config({Tag, Val}, InclDefs) -> emit(Tag, Val, undefined, InclDefs); @@ -279,7 +279,7 @@ gen_rel(Rel, Sys) -> {error, Text} end. -do_gen_rel(#rel{name = RelName, vsn = RelVsn}, +do_gen_rel(#rel{name = RelName, vsn = RelVsn, rel_apps = RelApps}, #sys{apps = Apps}, MergedApps) -> ErtsName = erts, @@ -288,7 +288,7 @@ do_gen_rel(#rel{name = RelName, vsn = RelVsn}, {release, {RelName, RelVsn}, {ErtsName, Erts#app.vsn}, - [strip_rel_info(App) || App <- MergedApps]}; + [strip_rel_info(App, RelApps) || App <- MergedApps]}; false -> reltool_utils:throw_error("Mandatory application ~p is " "not included", @@ -298,13 +298,17 @@ do_gen_rel(#rel{name = RelName, vsn = RelVsn}, strip_rel_info(#app{name = Name, vsn = Vsn, app_type = Type, - info = #app_info{incl_apps = InclApps}}) - when Type =/= undefined -> - case {Type, InclApps} of - {permanent, []} -> {Name, Vsn}; - {permanent, _} -> {Name, Vsn, InclApps}; - {_, []} -> {Name, Vsn, Type}; - {_, _} -> {Name, Vsn, Type, InclApps} + info = #app_info{incl_apps = AppInclApps}}, + RelApps) when Type =/= undefined -> + RelInclApps = case lists:keyfind(Name,#rel_app.name,RelApps) of + #rel_app{incl_apps = RIA} when RIA =/= undefined -> RIA; + _ -> undefined + end, + case {Type, RelInclApps} of + {permanent, undefined} -> {Name, Vsn}; + {permanent, _} -> {Name, Vsn, AppInclApps}; + {_, undefined} -> {Name, Vsn, Type}; + {_, _} -> {Name, Vsn, Type, AppInclApps} end. merge_apps(#rel{name = RelName, @@ -323,7 +327,7 @@ merge_apps(#rel{name = RelName, A#app.name =/= ?MISSING_APP_NAME, not lists:keymember(A#app.name, #app.name, MergedApps2)], MergedApps3 = do_merge_apps(RelName, Embedded, Apps, EmbAppType, MergedApps2), - sort_apps(MergedApps3). + sort_apps(lists:reverse(MergedApps3)). do_merge_apps(RelName, [#rel_app{name = Name} = RA | RelApps], Apps, RelAppType, Acc) -> case is_already_merged(Name, RelApps, Acc) of @@ -341,25 +345,18 @@ do_merge_apps(RelName, [Name | RelApps], Apps, RelAppType, Acc) -> true -> do_merge_apps(RelName, RelApps, Apps, RelAppType, Acc); false -> - RelApp = init_rel_app(Name, Apps), + RelApp = #rel_app{name = Name}, do_merge_apps(RelName, [RelApp | RelApps], Apps, RelAppType, Acc) end; do_merge_apps(_RelName, [], _Apps, _RelAppType, Acc) -> - lists:reverse(Acc). - -init_rel_app(Name, Apps) -> - {value, App} = lists:keysearch(Name, #app.name, Apps), - Info = App#app.info, - #rel_app{name = Name, - app_type = undefined, - incl_apps = Info#app_info.incl_apps}. + Acc. merge_app(RelName, - #rel_app{name = Name, - app_type = Type, - incl_apps = InclApps}, - RelAppType, - App) -> + #rel_app{name = Name, + app_type = Type, + incl_apps = InclApps0}, + RelAppType, + App) -> Type2 = case {Type, App#app.app_type} of {undefined, undefined} -> RelAppType; @@ -367,6 +364,11 @@ merge_app(RelName, {_, _} -> Type end, Info = App#app.info, + InclApps = + case InclApps0 of + undefined -> Info#app_info.incl_apps; + _ -> InclApps0 + end, case InclApps -- Info#app_info.incl_apps of [] -> App#app{app_type = Type2, info = Info#app_info{incl_apps = InclApps}}; @@ -421,7 +423,10 @@ do_gen_script(#rel{name = RelName, vsn = RelVsn}, Mandatory = mandatory_modules(), Early = Mandatory ++ Preloaded, {value, KernelApp} = lists:keysearch(kernel, #app.name, MergedApps), - InclApps = [I || #app{info = #app_info{incl_apps = I}} <- MergedApps], + InclApps = lists:flatmap(fun(#app{info = #app_info{incl_apps = I}}) -> + I + end, + MergedApps), %% Create the script DeepList = @@ -471,7 +476,7 @@ load_app_mods(#app{mods = Mods} = App, Mand, PathFlag, Variables) -> Path = cr_path(App, PathFlag, Variables), PartNames = lists:sort([{packages:split(M),M} || - #mod{name = M} <- Mods, + #mod{name = M, is_included=true} <- Mods, not lists:member(M, Mand)]), SplitMods = lists:foldl( @@ -513,7 +518,12 @@ sort_apps([#app{name = Name, info = Info} = App | Apps], Circular, Visited) -> {Uses, Apps1, NotFnd1} = - find_all(Name, Info#app_info.applications, Apps, Visited, [], []), + find_all(Name, + lists:reverse(Info#app_info.applications), + Apps, + Visited, + [], + []), {Incs, Apps2, NotFnd2} = find_all(Name, lists:reverse(Info#app_info.incl_apps), diff --git a/lib/reltool/src/reltool_utils.erl b/lib/reltool/src/reltool_utils.erl index 9cf9bd1418..3e50324011 100644 --- a/lib/reltool/src/reltool_utils.erl +++ b/lib/reltool/src/reltool_utils.erl @@ -126,18 +126,14 @@ prim_parse(Tokens, Acc) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% default_rels() -> - %%Kernel = #rel_app{name = kernel, incl_apps = []}, - %%Stdlib = #rel_app{name = stdlib, incl_apps = []}, - Sasl = #rel_app{name = sasl, incl_apps = []}, + %% kernel and stdlib are added automatically in every release [ #rel{name = ?DEFAULT_REL_NAME, vsn = "1.0", rel_apps = []}, - %%rel_apps = [Kernel, Stdlib]}, #rel{name = "start_sasl", vsn = "1.0", - rel_apps = [Sasl]} - %%rel_apps = [Kernel, Sasl, Stdlib]} + rel_apps = [#rel_app{name = sasl}]} ]. choose_default(Tag, Profile, InclDefs) diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl index 57b04251d4..122880fca9 100644 --- a/lib/reltool/test/reltool_server_SUITE.erl +++ b/lib/reltool/test/reltool_server_SUITE.erl @@ -283,7 +283,7 @@ create_release(_Config) -> %% started before the including application. %% Circular dependencies shall also be detected and cause error. -create_release_sort(_Config) -> {skip, "Multiple known problems - see OTP-9792"}; +create_release_sort(_Config) -> {skip, "Two bugs related to sorting"}; create_release_sort(Config) -> DataDir = ?config(data_dir,Config), %% Configure the server @@ -296,7 +296,11 @@ create_release_sort(Config) -> RelName7 = "Circular", RelName8 = "Include-both-missing-app", RelName9 = "Include-overwrite", + RelName10= "Uses-order-as-rel", RelVsn = "1.0", + %% Application z (.app file): + %% includes [tools, mnesia] + %% uses [kernel, stdlib, sasl, inets] Sys = {sys, [ @@ -304,19 +308,21 @@ create_release_sort(Config) -> {boot_rel, RelName1}, {rel, RelName1, RelVsn, [stdlib, kernel, mnesia, sasl]}, {rel, RelName2, RelVsn, [stdlib, kernel, sasl, mnesia]}, - {rel, RelName3, RelVsn, [stdlib, kernel, {z,[tools]}, tools]}, - {rel, RelName4, RelVsn, [stdlib, kernel, z, tools]}, + {rel, RelName3, RelVsn, [stdlib, kernel, {z,[tools]}, tools, mnesia]}, + {rel, RelName4, RelVsn, [stdlib, kernel, z, mnesia, tools]}, {rel, RelName5, RelVsn, [stdlib, kernel, {sasl,[tools]}]}, - {rel, RelName6, RelVsn, [stdlib, kernel, z]}, %z includes tools in .app + {rel, RelName6, RelVsn, [stdlib, kernel, z]}, {rel, RelName7, RelVsn, [stdlib, kernel, mnesia, y, sasl, x]}, {rel, RelName8, RelVsn, [stdlib, kernel, {z,[tools]}]}, {rel, RelName9, RelVsn, [stdlib, kernel, {z,[]}]}, + {rel, RelName10, RelVsn, [stdlib, kernel, {z,[]}, inets, sasl]}, {incl_cond,exclude}, {mod_cond,app}, {app,kernel,[{incl_cond,include}]}, {app,stdlib,[{incl_cond,include}]}, {app,mnesia,[{incl_cond,include}]}, {app,sasl,[{incl_cond,include}]}, + {app,inets,[{incl_cond,include}]}, {app,x,[{incl_cond,include}]}, {app,y,[{incl_cond,include}]}, {app,z,[{incl_cond,include}]}, @@ -324,7 +330,6 @@ create_release_sort(Config) -> ]}, %% Generate release - %% BUG: reltool reverses the list of applications after kernel and stdlib ?msym({ok, {release, {RelName1, RelVsn}, {erts, _}, [{kernel, _}, @@ -333,7 +338,6 @@ create_release_sort(Config) -> {sasl, _}]}}, reltool:get_rel([{config, Sys}], RelName1)), - %% BUG: reltool reverses the list of applications after kernel and stdlib ?msym({ok, {release, {RelName2, RelVsn}, {erts, _}, [{kernel, _}, @@ -346,19 +350,21 @@ create_release_sort(Config) -> {erts, _}, [{kernel, _}, {stdlib, _}, + {sasl, _}, + {inets, _}, {tools, _}, - {z, _, [tools]}]}}, + {z, _, [tools]}, + {mnesia, _}]}}, reltool:get_rel([{config, Sys}], RelName3)), - %% BUG: reltool does not honor included applications in .app files - %% unless they are also mentioned in the 'rel' specification in - %% the reltool config. - %% => order of tools and z does not become correct in rel (tools - %% should be first since it is included in z) + %%! BUG: same as OTP-4121, but for reltool???? Or revert tools and mnesia ?msym({ok, {release, {RelName4, RelVsn}, {erts, _}, [{kernel, _}, {stdlib, _}, + {sasl, _}, + {inets, _}, + {mnesia, _}, {tools, _}, {z, _}]}}, reltool:get_rel([{config, Sys}], RelName4)), @@ -368,11 +374,7 @@ create_release_sort(Config) -> "in the app file: [tools]"}, reltool:get_rel([{config, Sys}], RelName5)), - %% BUG: reltool does not honor included applications in .app files - %% unless they are also mentioned in the 'rel' specification in - %% the reltool config. - %% => does not detect that tools (included in z) is missing - ?m({error, "Undefined applications: [tools]"}, + ?m({error, "Undefined applications: [tools,mnesia]"}, reltool:get_rel([{config, Sys}], RelName6)), ?m({error,"Circular dependencies: [x,y]"}, @@ -381,15 +383,25 @@ create_release_sort(Config) -> ?m({error,"Undefined applications: [tools]"}, reltool:get_rel([{config, Sys}], RelName8)), - %% BUG: Reltool looses the empty include list for z, which should - %% overwrite included_applications statement from the .app file. ?msym({ok,{release,{RelName9,RelVsn}, {erts,_}, [{kernel,_}, {stdlib,_}, + {sasl, _}, + {inets, _}, {z,_,[]}]}}, reltool:get_rel([{config, Sys}], RelName9)), + %%! BUG: same as OTP-9984, but for reltool???? Or revert inets and sasl? + ?msym({ok,{release,{RelName10,RelVsn}, + {erts,_}, + [{kernel,_}, + {stdlib,_}, + {inets, _}, + {sasl, _}, + {z,_,[]}]}}, + reltool:get_rel([{config, Sys}], RelName10)), + ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -442,7 +454,7 @@ create_script(_Config) -> %% Test creation of .script with different sorting of applications and %% included applications. %% Test that result is equal to what systools produces -create_script_sort(_Config) -> {skip, "Multiple known problems - see OTP-9792"}; +create_script_sort(_Config) -> {skip, "OTP-9984 - stdlib sort problem"}; create_script_sort(Config) -> DataDir = ?config(data_dir,Config), %% Configure the server @@ -464,10 +476,10 @@ create_script_sort(Config) -> {boot_rel, RelName1}, {rel, RelName1, RelVsn, [stdlib, kernel, mnesia, sasl]}, {rel, RelName2, RelVsn, [stdlib, kernel, sasl, mnesia]}, - {rel, RelName3, RelVsn, [stdlib, kernel, {z,[tools]}, tools]}, - {rel, RelName4, RelVsn, [stdlib, kernel, z, tools]}, + {rel, RelName3, RelVsn, [stdlib, kernel, {z,[tools]}, tools, mnesia]}, + {rel, RelName4, RelVsn, [stdlib, kernel, z, mnesia, tools]}, {rel, RelName5, RelVsn, [stdlib, kernel, {sasl,[tools]}]}, - {rel, RelName6, RelVsn, [stdlib, kernel, z]}, %z includes tools in .app + {rel, RelName6, RelVsn, [stdlib, kernel, z]}, {rel, RelName7, RelVsn, [stdlib, kernel, mnesia, y, sasl, x]}, {rel, RelName8, RelVsn, [stdlib, kernel, {z,[tools]}]}, {rel, RelName9, RelVsn, [stdlib, kernel, {z,[]}]}, @@ -477,6 +489,7 @@ create_script_sort(Config) -> {app,stdlib,[{incl_cond,include}]}, {app,mnesia,[{incl_cond,include}]}, {app,sasl,[{incl_cond,include}]}, + {app,inets,[{incl_cond,include}]}, {app,x,[{incl_cond,include}]}, {app,y,[{incl_cond,include}]}, {app,z,[{incl_cond,include}]}, @@ -487,11 +500,13 @@ create_script_sort(Config) -> %% Generate release files application:load(sasl), + application:load(inets), application:load(mnesia), application:load(tools), {ok,KernelVsn} = application:get_key(kernel,vsn), {ok,StdlibVsn} = application:get_key(stdlib,vsn), {ok,SaslVsn} = application:get_key(sasl,vsn), + {ok,InetsVsn} = application:get_key(inets,vsn), {ok,MnesiaVsn} = application:get_key(mnesia,vsn), {ok,ToolsVsn} = application:get_key(tools,vsn), ErtsVsn = erlang:system_info(version), @@ -514,14 +529,20 @@ create_script_sort(Config) -> [{kernel,KernelVsn}, {stdlib,StdlibVsn}, {z,"1.0",[tools]}, - {tools,ToolsVsn}]}, + {tools,ToolsVsn}, + {mnesia,MnesiaVsn}, + {sasl,SaslVsn}, + {inets,InetsVsn}]}, FullName3 = filename:join(?WORK_DIR,RelName3), ?m(ok, file:write_file(FullName3 ++ ".rel", io_lib:format("~p.\n", [Rel3]))), Rel4 = {release, {RelName4,RelVsn}, {erts,ErtsVsn}, [{kernel,KernelVsn}, {stdlib,StdlibVsn}, {z,"1.0"}, - {tools,ToolsVsn}]}, + {tools,ToolsVsn}, + {mnesia,MnesiaVsn}, + {sasl,SaslVsn}, + {inets,InetsVsn}]}, FullName4 = filename:join(?WORK_DIR,RelName4), ?m(ok, file:write_file(FullName4 ++ ".rel", io_lib:format("~p.\n", [Rel4]))), Rel5 = {release, {RelName5,RelVsn}, {erts,ErtsVsn}, @@ -555,42 +576,30 @@ create_script_sort(Config) -> Rel9 = {release, {RelName9,RelVsn}, {erts,ErtsVsn}, [{kernel,KernelVsn}, {stdlib,StdlibVsn}, - {z,"1.0",[]}]}, + {z,"1.0",[]}, + {sasl,SaslVsn}, + {inets,InetsVsn}]}, FullName9 = filename:join(?WORK_DIR,RelName9), ?m(ok, file:write_file(FullName9 ++ ".rel", io_lib:format("~p.\n", [Rel9]))), %% Generate script files with systools and reltool and compare ZPath = filename:join([LibDir,"*",ebin]), - %% BUG: reltool reverses the list of applications after kernel and stdlib - %% => mnesia and sasl are reverted ?msym({ok,_,_}, systools_make_script(FullName1,ZPath)), {ok, [SystoolsScript1]} = ?msym({ok,[_]}, file:consult(FullName1++".script")), {ok, Script1} = ?msym({ok, _}, reltool:get_script(Pid, RelName1)), ?m(equal, diff_script(SystoolsScript1, Script1)), - %% BUG: reltool reverses the list of applications after kernel and stdlib - %% => mnesia and sasl are reverted ?msym({ok,_,_}, systools_make_script(FullName2,ZPath)), {ok, [SystoolsScript2]} = ?msym({ok,[_]}, file:consult(FullName2++".script")), {ok, Script2} = ?msym({ok, _}, reltool:get_script(Pid, RelName2)), ?m(equal, diff_script(SystoolsScript2, Script2)), - %% BUG1: reltool loads all modules in the ebin dir of an application, - %% even if mod_cond is set to 'app'. - %% BUG2: reltool shall not start included applications!! ?msym({ok,_,_}, systools_make_script(FullName3,ZPath)), {ok, [SystoolsScript3]} = ?msym({ok,[_]}, file:consult(FullName3++".script")), {ok, Script3} = ?msym({ok, _}, reltool:get_script(Pid, RelName3)), ?m(equal, diff_script(SystoolsScript3, Script3)), - %% BUG1: reltool loads all modules in the ebin dir of an application, - %% even if mod_cond is set to 'app'. - %% BUG2: reltool does not honor included applications in .app files - %% unless they are also mentioned in the 'rel' specification in - %% the reltool config. - %% => faulty order of load instructions for tools and z. tools - %% should be first since it is included in z. ?msym({ok,_,_}, systools_make_script(FullName4,ZPath)), {ok, [SystoolsScript4]} = ?msym({ok,[_]}, file:consult(FullName4++".script")), {ok, Script4} = ?msym({ok, _}, reltool:get_script(Pid, RelName4)), @@ -603,13 +612,9 @@ create_script_sort(Config) -> "in the app file: [tools]"}, reltool:get_script(Pid, RelName5)), - %% BUG: reltool does not honor included applications in .app files - %% unless they are also mentioned in the 'rel' specification in - %% the reltool config. - %% => does not detect that tools (included in z) is missing ?msym({error,_,{undefined_applications,_}}, systools_make_script(FullName6,ZPath)), - ?m({error, "Undefined applications: [tools]"}, + ?m({error, "Undefined applications: [tools,mnesia]"}, reltool:get_script(Pid, RelName6)), ?msym({error,_,{circular_dependencies,_}}, diff --git a/lib/reltool/test/reltool_server_SUITE_data/sort_apps/z-1.0/ebin/z.app b/lib/reltool/test/reltool_server_SUITE_data/sort_apps/z-1.0/ebin/z.app index 1622975bf6..8608bc554b 100644 --- a/lib/reltool/test/reltool_server_SUITE_data/sort_apps/z-1.0/ebin/z.app +++ b/lib/reltool/test/reltool_server_SUITE_data/sort_apps/z-1.0/ebin/z.app @@ -4,5 +4,5 @@ {vsn, "1.0"}, {modules,[]}, {registered, []}, - {applications, [kernel, stdlib]}, - {included_applications, [tools]}]}. + {applications, [kernel, stdlib, sasl, inets]}, + {included_applications, [tools, mnesia]}]}. -- cgit v1.2.3 From f294d207da75e950bc533cb9d7d9148390c1001a Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 16 Mar 2012 09:57:13 +0100 Subject: [sasl] Doc that included applications in .rel defaults to same as in .app OTP-9980 Sasl documentation earlier said that the InclApps parameters in a .rel file defaults to the empty list. This is not correct. It defaults to the same value as specified in the .app file. --- lib/sasl/doc/src/rel.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/sasl/doc/src/rel.xml b/lib/sasl/doc/src/rel.xml index 470adf3c03..68ef90330f 100644 --- a/lib/sasl/doc/src/rel.xml +++ b/lib/sasl/doc/src/rel.xml @@ -5,7 +5,7 @@
1997 - 2011 + 2012 Ericsson AB, All Rights Reserved @@ -88,7 +88,7 @@

The list must be a subset of the included applications specified in the application resource file (Application.app) and overrides this value. Defaults - to the empty list.

+ to the same value as in the application resource file.

-- cgit v1.2.3 From 8233c6e1787fa8e81f0301f10aea82c695cd322a Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 16 Mar 2012 14:34:30 +0100 Subject: [sasl] Sort applications used by other applications correctly in .script OTP-9984 Applications that are listed in {applications,Apps} in the app file were not sorted correctly in the script file. They were loaded/started in the reverse order of how they were listed in the .app file. This is corrected so they are now sorted (internally between each other) in the same way as they are listed in the .rel file. --- lib/sasl/src/systools_make.erl | 60 +++++++++++------- lib/sasl/test/systools_SUITE.erl | 133 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 166 insertions(+), 27 deletions(-) (limited to 'lib') diff --git a/lib/sasl/src/systools_make.erl b/lib/sasl/src/systools_make.erl index 12ba2a5476..3c1002e4a6 100644 --- a/lib/sasl/src/systools_make.erl +++ b/lib/sasl/src/systools_make.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-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 @@ -375,7 +375,7 @@ get_release1(File, Path, ModTestP, Machine) -> {ok, Release, Warnings1} = read_release(File, Path), {ok, Appls0} = collect_applications(Release, Path), {ok, Appls1} = check_applications(Appls0), - {ok, Appls2} = sort_included_applications(Appls1, Release), % OTP-4121 + {ok, Appls2} = sort_used_and_incl_appls(Appls1, Release), % OTP-4121, OTP-9984 {ok, Warnings2} = check_modules(Appls2, Path, ModTestP, Machine), {ok, Appls} = sort_appls(Appls2), {ok, Release, Appls, Warnings1 ++ Warnings2}. @@ -842,34 +842,45 @@ undefined_applications(Appls) -> filter(fun(X) -> not member(X, Defined) end, Uses). %%______________________________________________________________________ -%% sort_included_applications(Applications, Release) -> Applications +%% sort_used_and_incl_appls(Applications, Release) -> Applications %% Applications = [{{Name,Vsn},#application}] %% Release = #release{} %% -%% Check that included applications are given in the same order as in -%% the release resource file (.rel). Otherwise load instructions in -%% the boot script, and consequently release upgrade instructions in -%% relup, may end up in the wrong order. +%% OTP-4121, OTP-9984 +%% Check that used and included applications are given in the same +%% order as in the release resource file (.rel). Otherwise load and +%% start instructions in the boot script, and consequently release +%% upgrade instructions in relup, may end up in the wrong order. -sort_included_applications(Applications, Release) when is_tuple(Release) -> +sort_used_and_incl_appls(Applications, Release) when is_tuple(Release) -> {ok, - sort_included_applications(Applications, Release#release.applications)}; - -sort_included_applications([{Tuple,Appl}|Appls], OrderedAppls) -> - case Appl#application.includes of - Incls when length(Incls)>1 -> - IndexedIncls = find_pos(Incls, OrderedAppls), - SortedIndexedIncls = lists:keysort(1, IndexedIncls), - Incls2 = lists:map(fun({_Index,Name}) -> Name end, - SortedIndexedIncls), - Appl2 = Appl#application{includes=Incls2}, - [{Tuple,Appl2}|sort_included_applications(Appls, OrderedAppls)]; - _Incls -> - [{Tuple,Appl}|sort_included_applications(Appls, OrderedAppls)] - end; -sort_included_applications([], _OrderedAppls) -> + sort_used_and_incl_appls(Applications, Release#release.applications)}; + +sort_used_and_incl_appls([{Tuple,Appl}|Appls], OrderedAppls) -> + Incls2 = + case Appl#application.includes of + Incls when length(Incls)>1 -> + sort_appl_list(Incls, OrderedAppls); + Incls -> + Incls + end, + Uses2 = + case Appl#application.uses of + Uses when length(Uses)>1 -> + sort_appl_list(Uses, OrderedAppls); + Uses -> + Uses + end, + Appl2 = Appl#application{includes=Incls2, uses=Uses2}, + [{Tuple,Appl2}|sort_used_and_incl_appls(Appls, OrderedAppls)]; +sort_used_and_incl_appls([], _OrderedAppls) -> []. +sort_appl_list(List, Order) -> + IndexedList = find_pos(List, Order), + SortedIndexedList = lists:keysort(1, IndexedList), + lists:map(fun({_Index,Name}) -> Name end, SortedIndexedList). + find_pos([Name|Incs], OrderedAppls) -> [find_pos(1, Name, OrderedAppls)|find_pos(Incs, OrderedAppls)]; find_pos([], _OrderedAppls) -> @@ -1253,7 +1264,8 @@ sort_appls(Appls) -> {ok, sort_appls(Appls, [], [], [])}. sort_appls([{N, A}|T], Missing, Circular, Visited) -> {Name,_Vsn} = N, - {Uses, T1, NotFnd1} = find_all(Name, A#application.uses, T, Visited, [], []), + {Uses, T1, NotFnd1} = find_all(Name, lists:reverse(A#application.uses), + T, Visited, [], []), {Incs, T2, NotFnd2} = find_all(Name, lists:reverse(A#application.includes), T1, Visited, [], []), Missing1 = NotFnd1 ++ NotFnd2 ++ Missing, diff --git a/lib/sasl/test/systools_SUITE.erl b/lib/sasl/test/systools_SUITE.erl index 4cf7364d74..72b3eb8954 100644 --- a/lib/sasl/test/systools_SUITE.erl +++ b/lib/sasl/test/systools_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011. All Rights Reserved. +%% Copyright Ericsson AB 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 @@ -45,7 +45,7 @@ abnormal_script/1, src_tests_script/1, crazy_script/1, included_script/1, included_override_script/1, included_fail_script/1, included_bug_script/1, exref_script/1, - otp_3065_circular_dependenies/1]). + otp_3065_circular_dependenies/1, included_and_used_sort_script/1]). -export([tar_options/1, normal_tar/1, no_mod_vsn_tar/1, system_files_tar/1, system_files_tar/2, invalid_system_files_tar/1, invalid_system_files_tar/2, variable_tar/1, @@ -80,7 +80,7 @@ groups() -> no_sasl_script, src_tests_script, crazy_script, included_script, included_override_script, included_fail_script, included_bug_script, exref_script, - otp_3065_circular_dependenies]}, + otp_3065_circular_dependenies, included_and_used_sort_script]}, {tar, [], [tar_options, normal_tar, no_mod_vsn_tar, system_files_tar, invalid_system_files_tar, variable_tar, @@ -600,6 +600,24 @@ otp_3065_circular_dependenies(Config) when is_list(Config) -> ok = file:set_cwd(OldDir), ok. +%% Test sorting of included applications and used applications +included_and_used_sort_script(Config) when is_list(Config) -> + {ok, OldDir} = file:get_cwd(), + {LatestDir1, LatestName1} = create_include_files(sort_apps, Config), + ok = file:set_cwd(LatestDir1), + ok = systools:make_script(LatestName1), + ok = check_include_script(LatestName1, + [t20,t19,t18,t17,t16,t15,t14],[t20,t19,t18,t14]), + + {LatestDir2, LatestName2} = create_include_files(sort_apps_rev, Config), + ok = file:set_cwd(LatestDir2), + ok = systools:make_script(LatestName2), + ok = check_include_script(LatestName2, + [t18,t19,t20,t15,t16,t17,t14],[t18,t19,t20,t14]), + + ok = file:set_cwd(OldDir), + ok. + %% make_script: Check that make_script exref option works. exref_script(Config) when is_list(Config) -> @@ -2301,8 +2319,53 @@ create_include_files(otp_3065_circular_dependenies, Config) -> " {chAts, \"1.0\"}, {aa12, \"1.0\"}, \n" " {chTraffic, \"1.0\"}]}.\n", file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(sort_apps, Config) -> + PrivDir = ?privdir, + Name = fname(PrivDir, sort_apps), + create_sort_apps(PrivDir), + + Apps = application_controller:which_applications(), + {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t14, \"1.0\"}, \n" + " {t20, \"1.0\"}, \n" + " {t19, \"1.0\"}, \n" + " {t18, \"1.0\"}, \n" + " {t17, \"1.0\"}, \n" + " {t16, \"1.0\"}, \n" + " {t15, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(sort_apps_rev, Config) -> + PrivDir = ?privdir, + Name = fname(PrivDir, sort_apps_rev), + create_sort_apps(PrivDir), + + Apps = application_controller:which_applications(), + {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t14, \"1.0\"}, \n" + " {t18, \"1.0\"}, \n" + " {t19, \"1.0\"}, \n" + " {t20, \"1.0\"}, \n" + " {t15, \"1.0\"}, \n" + " {t16, \"1.0\"}, \n" + " {t17, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), {filename:dirname(Name), filename:basename(Name)}. + create_apps(Dir) -> T1 = "{application, t1,\n" " [{vsn, \"1.0\"},\n" @@ -2451,6 +2514,70 @@ create_apps_3065(Dir) -> " {registered, []}]}.\n", file:write_file(fname(Dir, 'aa12.app'), list_to_binary(T13)). +create_sort_apps(Dir) -> + T14 = "{application, t14,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, [t18,t20,t19]},\n" + " {included_applications, [t15,t17,t16]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't14.app'), list_to_binary(T14)), + + T15 = "{application, t15,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't15.app'), list_to_binary(T15)), + + T16 = "{application, t16,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't16.app'), list_to_binary(T16)), + + T17 = "{application, t17,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't17.app'), list_to_binary(T17)), + + T18 = "{application, t18,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't18.app'), list_to_binary(T18)), + + T19 = "{application, t19,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't19.app'), list_to_binary(T19)), + + T20 = "{application, t20,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't20.app'), list_to_binary(T20)). + fname(N) -> filename:join(N). -- cgit v1.2.3 From 20ef6a7d381b37d7c620f303a981a3a8b4e227a8 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 19 Mar 2012 09:06:38 +0100 Subject: [reltool] Remove skip statement for test that failed due to stdlib error The stdlib error has been corrected in a previous commit so the failing test (reltool_server_SUITE:create_script_sort) can be un-skipped. --- lib/reltool/test/reltool_server_SUITE.erl | 1 - 1 file changed, 1 deletion(-) (limited to 'lib') diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl index 122880fca9..4e24a2fb55 100644 --- a/lib/reltool/test/reltool_server_SUITE.erl +++ b/lib/reltool/test/reltool_server_SUITE.erl @@ -454,7 +454,6 @@ create_script(_Config) -> %% Test creation of .script with different sorting of applications and %% included applications. %% Test that result is equal to what systools produces -create_script_sort(_Config) -> {skip, "OTP-9984 - stdlib sort problem"}; create_script_sort(Config) -> DataDir = ?config(data_dir,Config), %% Configure the server -- cgit v1.2.3 From 844afaf366ad7a4f668a60e896d0ae3b9e5d850b Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 19 Mar 2012 15:54:30 +0100 Subject: [reltool] Fix GUI problems on Windows The new warning list did not work on Windows. It could not display tooltips for each warning and the popup window would always disappear behind the main system window. Also, column width did not occur well initially in list controls. --- lib/reltool/src/reltool_app_win.erl | 4 +- lib/reltool/src/reltool_sys_win.erl | 83 +++++++++++++++++---------- lib/reltool/src/reltool_utils.erl | 21 +++++++ lib/reltool/test/reltool_manual_gui_SUITE.erl | 13 +++-- 4 files changed, 82 insertions(+), 39 deletions(-) (limited to 'lib') diff --git a/lib/reltool/src/reltool_app_win.erl b/lib/reltool/src/reltool_app_win.erl index 70bd72b258..eddb37ea11 100644 --- a/lib/reltool/src/reltool_app_win.erl +++ b/lib/reltool/src/reltool_app_win.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2010. 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 @@ -271,8 +271,8 @@ create_apps_list_ctrl(Panel, Sizer, Text) -> ListItem = wxListItem:new(), wxListItem:setAlign(ListItem, ?wxLIST_FORMAT_LEFT), wxListItem:setText(ListItem, Text), + wxListItem:setWidth(ListItem, reltool_utils:get_column_width(ListCtrl)), wxListCtrl:insertColumn(ListCtrl, ?APPS_APP_COL, ListItem), - %% wxListCtrl:setColumnWidth(ListCtrl, ?APPS_APP_COL, ?APPS_APP_COL_WIDTH), wxListItem:destroy(ListItem), wxSizer:add(Sizer, ListCtrl, diff --git a/lib/reltool/src/reltool_sys_win.erl b/lib/reltool/src/reltool_sys_win.erl index 29a01b63d8..912a47ec39 100644 --- a/lib/reltool/src/reltool_sys_win.erl +++ b/lib/reltool/src/reltool_sys_win.erl @@ -458,6 +458,7 @@ create_app_list_ctrl(Panel, OuterSz, Title, Tick, Cross) -> ListItem = wxListItem:new(), wxListItem:setAlign(ListItem, ?wxLIST_FORMAT_LEFT), wxListItem:setText(ListItem, Title), + wxListItem:setWidth(ListItem, reltool_utils:get_column_width(ListCtrl)), wxListCtrl:insertColumn(ListCtrl, ?APPS_APP_COL, ListItem), wxListItem:destroy(ListItem), @@ -665,7 +666,8 @@ create_warning_list(#state{panel = Panel} = S) -> {size, {?WIN_WIDTH,80}}]), reltool_utils:assign_image_list(ListCtrl), wxListCtrl:insertColumn(ListCtrl, ?WARNING_COL, "Warnings", - [{format,?wxLIST_FORMAT_LEFT}]), + [{format,?wxLIST_FORMAT_LEFT}, + {width,reltool_utils:get_column_width(ListCtrl)}]), wxListCtrl:setToolTip(ListCtrl, ?DEFAULT_WARNING_TIP), wxEvtHandler:connect(ListCtrl, size, [{skip, true}, {userData, warnings}]), @@ -907,7 +909,9 @@ handle_event(S, #wx{id = Id, obj= ObjRef, userData = UserData, event = Event} = when S#state.popup_menu =/= undefined -> handle_popup_event(S, Type, Id, ObjRef, UserData, Str); #wxMouse{type = enter_window} -> - wxWindow:setFocus(ObjRef), + %% The following is commented out because it raises the + %% main system window on top of popup windows. + %% wxWindow:setFocus(ObjRef), S; _ -> case wxNotebook:getPageText(S#state.book, @@ -920,21 +924,12 @@ handle_event(S, #wx{id = Id, obj= ObjRef, userData = UserData, event = Event} = end. handle_warning_event(S, ObjRef, _, #wxSize{type = size}) -> - {Total, _} = wxWindow:getClientSize(ObjRef), - SBSize = scroll_size(ObjRef), - wxListCtrl:setColumnWidth(ObjRef, ?WARNING_COL, Total-SBSize), + ColumnWidth = reltool_utils:get_column_width(ObjRef), + wxListCtrl:setColumnWidth(ObjRef, ?WARNING_COL, ColumnWidth), S; handle_warning_event(S, ObjRef, _, #wxMouse{type = motion, x=X, y=Y}) -> Pos = reltool_utils:wait_for_stop_motion(ObjRef, {X,Y}), - Index = wxListCtrl:findItem(ObjRef,-1,Pos,0), - Tip = - case wxListCtrl:getItemText(ObjRef,Index) of - "" -> - ?DEFAULT_WARNING_TIP; - Text -> - "WARNING:\n" ++ Text - end, - wxListCtrl:setToolTip(ObjRef, Tip), + warning_list_set_tool_tip(os:type(),ObjRef,Pos), S; handle_warning_event(S, ObjRef, _, #wxList{type = command_list_item_activated, itemIndex = Pos}) -> @@ -945,6 +940,49 @@ handle_warning_event(S, _ObjRef, {warning,Frame}, wxFrame:destroy(Frame), S#state{warning_wins = lists:delete(Frame,S#state.warning_wins)}. +warning_list_set_tool_tip({win32,_},ListCtrl,{_X,Y}) -> + case win_find_item(ListCtrl,Y,0) of + -1 -> + wxListCtrl:setToolTip(ListCtrl,?DEFAULT_WARNING_TIP); + _Index -> + %% The following is commented out because there seems to + %% be an utomatic tooltip under Windows that shows the + %% expanded list item in case it is truncated because it + %% is too long for column width. + %% Tip = + %% case wxListCtrl:getItemText(ListCtrl,Index) of + %% "" -> + %% ?DEFAULT_WARNING_TIP; + %% Text -> + %% "WARNING:\n" ++ Text + %% end, + %% wxListCtrl:setToolTip(ListCtrl,Tip), + ok + end; +warning_list_set_tool_tip(_,ListCtrl,Pos) -> + case wxListCtrl:findItem(ListCtrl,-1,Pos,0) of + Index when Index >= 0 -> + Tip = + case wxListCtrl:getItemText(ListCtrl,Index) of + "" -> + ?DEFAULT_WARNING_TIP; + Text -> + "WARNING:\n" ++ Text + end, + wxListCtrl:setToolTip(ListCtrl, Tip); + _ -> + ok + end. + +win_find_item(ListCtrl,YPos,Index) -> + case wxListCtrl:getItemRect(ListCtrl,Index) of + {true,{_,Y,_,H}} when YPos>=Y, YPos= + Index; + {true,_} -> + win_find_item(ListCtrl,YPos,Index+1); + {false,_} -> + -1 + end. display_warning(S,Warning) -> Pos = warning_popup_position(S,?WARNING_POPUP_SIZE), @@ -954,8 +992,6 @@ display_warning(S,Warning) -> Text = wxTextCtrl:new(Panel, ?wxID_ANY, [{value, Warning}, {style, TextStyle}, {size, ?WARNING_POPUP_SIZE}]), - Color = wxWindow:getBackgroundColour(Frame), - wxTextCtrl:setBackgroundColour(Text,Color), Attr = wxTextAttr:new(), wxTextAttr:setLeftIndent(Attr,10), wxTextAttr:setRightIndent(Attr,10), @@ -1608,21 +1644,6 @@ add_text(_,_,[]) -> ok. -scroll_size(ObjRef) -> - case os:type() of - {win32, nt} -> 0; - {unix, darwin} -> - %% I can't figure out is there is a visible scrollbar - %% Always make room for it - wxSystemSettings:getMetric(?wxSYS_VSCROLL_X); - _ -> - case wxWindow:hasScrollbar(ObjRef, ?wxVERTICAL) of - true -> wxSystemSettings:getMetric(?wxSYS_VSCROLL_X); - false -> 0 - end - end. - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% sys callbacks diff --git a/lib/reltool/src/reltool_utils.erl b/lib/reltool/src/reltool_utils.erl index 3e50324011..b0def45213 100644 --- a/lib/reltool/src/reltool_utils.erl +++ b/lib/reltool/src/reltool_utils.erl @@ -29,6 +29,7 @@ app_dir_test/2, split_app_dir/1, get_item/1, get_items/1, get_selected_items/3, select_items/3, select_item/2, + get_column_width/1, safe_keysearch/5, print/4, add_warning/3, @@ -383,6 +384,26 @@ select_item(ListCtrl, [{ItemNo, Text} | Items]) -> select_item(_ListCtrl, []) -> ok. +get_column_width(ListCtrl) -> + wx:batch(fun() -> + {Total, _} = wxWindow:getClientSize(ListCtrl), + Total - scroll_size(ListCtrl) + end). + +scroll_size(ObjRef) -> + case os:type() of + {win32, nt} -> 0; + {unix, darwin} -> + %% I can't figure out is there is a visible scrollbar + %% Always make room for it + wxSystemSettings:getMetric(?wxSYS_VSCROLL_X); + _ -> + case wxWindow:hasScrollbar(ObjRef, ?wxVERTICAL) of + true -> wxSystemSettings:getMetric(?wxSYS_VSCROLL_X); + false -> 0 + end + end. + safe_keysearch(Key, Pos, List, Mod, Line) -> case lists:keysearch(Key, Pos, List) of false -> diff --git a/lib/reltool/test/reltool_manual_gui_SUITE.erl b/lib/reltool/test/reltool_manual_gui_SUITE.erl index 1ebee9fae1..0dcc5cbf15 100644 --- a/lib/reltool/test/reltool_manual_gui_SUITE.erl +++ b/lib/reltool/test/reltool_manual_gui_SUITE.erl @@ -97,14 +97,14 @@ config(Config) -> break("the warning text can be marked, copied and pasted", "close the popup with the close box on the top frame"), break("it disappears", - "select application a from 'Included' column and click red cross to " - "exclude it"), + "select application a from 'Included' column and click 'cross'-button " + "with to exclude it"), break("application a is moved to 'Excluded' column", - "select application tools from 'Excluded' column and click green V to " - "include it"), + "select application tools from 'Excluded' column and click " + "'tick'-button to include it"), break("application tools is moved to 'Included' column", "select application runtime_tools from 'Excluded' column and click " - "green V to include it"), + "'tick'-button to include it"), break("application runtime_tools is moved to 'Included' column", "undo"), @@ -171,7 +171,8 @@ config(Config) -> {ok,ServerPid} = reltool:get_server(SysPid), unlink(SysPid), break("the system window is still alive", - "terminate reltool by hitting 'Ctrl-q' when system window is active"), + "terminate reltool by hitting 'Ctrl-q' (linux) or clicking the " + "close box on the top fram when system window is active"), false = erlang:is_process_alive(SysPid), false = erlang:is_process_alive(ServerPid), -- cgit v1.2.3 From e67e94c20f16a102da93a40cfe4edf7885a602d8 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Tue, 20 Mar 2012 11:33:18 +0100 Subject: [reltool] Update status bar for all config changes and for generate target OTP-9792 For some configuration changes and during generation of target system, there was no indication in the status bar that reltool was working. This has been corrected - it now says "Processing libraries...". --- lib/reltool/src/reltool_sys_win.erl | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/reltool/src/reltool_sys_win.erl b/lib/reltool/src/reltool_sys_win.erl index 912a47ec39..0c0b295db1 100644 --- a/lib/reltool/src/reltool_sys_win.erl +++ b/lib/reltool/src/reltool_sys_win.erl @@ -1246,9 +1246,12 @@ handle_app_event(S, Event, ObjRef, UserData) -> [?MODULE, self(), ObjRef, UserData, Event]), S. -handle_app_button(#state{server_pid = ServerPid, app_wins = AppWins} = S, +handle_app_button(#state{server_pid = ServerPid, + status_bar = Bar, + app_wins = AppWins} = S, Items, Action) -> + wxStatusBar:setStatusText(Bar, "Processing libraries..."), NewApps = [move_app(S, Item, Action) || Item <- Items], case reltool_server:set_apps(ServerPid, NewApps) of {ok, _Warnings} -> @@ -1289,7 +1292,10 @@ move_app(S, {_ItemNo, AppBase}, Action) -> end, OldApp#app{incl_cond = AppCond}. -do_set_app(#state{server_pid = ServerPid, app_wins = AppWins} = S, NewApp) -> +do_set_app(#state{server_pid = ServerPid, + status_bar = Bar, + app_wins = AppWins} = S, NewApp) -> + wxStatusBar:setStatusText(Bar, "Processing libraries..."), Result = reltool_server:set_app(ServerPid, NewApp), ReturnApp = case Result of @@ -1489,26 +1495,28 @@ save_config(#state{config_file = OldFile} = S, InclDefaults, InclDerivates) -> S end. -gen_rel_files(#state{target_dir = OldDir} = S) -> +gen_rel_files(#state{status_bar = Bar, target_dir = OldDir} = S) -> Style = ?wxFD_SAVE bor ?wxFD_OVERWRITE_PROMPT, case select_dir(S#state.frame, "Select a directory to generate rel, script and boot files to", OldDir, Style) of {ok, NewDir} -> + wxStatusBar:setStatusText(Bar, "Processing libraries..."), Status = reltool_server:gen_rel_files(S#state.server_pid, NewDir), check_and_refresh(S, Status); cancel -> S end. -gen_target(#state{target_dir = OldDir} = S) -> +gen_target(#state{status_bar = Bar, target_dir = OldDir} = S) -> Style = ?wxFD_SAVE bor ?wxFD_OVERWRITE_PROMPT, case select_dir(S#state.frame, "Select a directory to generate a target system to", OldDir, Style) of {ok, NewDir} -> + wxStatusBar:setStatusText(Bar, "Processing libraries..."), Status = reltool_server:gen_target(S#state.server_pid, NewDir), check_and_refresh(S#state{target_dir = NewDir}, Status); cancel -> -- cgit v1.2.3 From e855e8326a6ce589991da9cafc2590f306d035c3 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Tue, 20 Mar 2012 11:47:48 +0100 Subject: [reltool] Improve title of dependency colum OTP-9792 The colum listing modules that uses the current application or module did earlier have the title "Modules used by others". This is now changed to "Modules using this". --- lib/reltool/src/reltool_app_win.erl | 2 +- lib/reltool/src/reltool_mod_win.erl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/reltool/src/reltool_app_win.erl b/lib/reltool/src/reltool_app_win.erl index eddb37ea11..e0acfab7aa 100644 --- a/lib/reltool/src/reltool_app_win.erl +++ b/lib/reltool/src/reltool_app_win.erl @@ -292,7 +292,7 @@ create_deps_page(S, Derived) -> UsedByCtrl = create_mods_list_ctrl(Panel, Main, - "Modules used by others", + "Modules using this", " and their applications", undefined, undefined), diff --git a/lib/reltool/src/reltool_mod_win.erl b/lib/reltool/src/reltool_mod_win.erl index 8cf175547b..899423bb6d 100644 --- a/lib/reltool/src/reltool_mod_win.erl +++ b/lib/reltool/src/reltool_mod_win.erl @@ -215,7 +215,7 @@ create_deps_page(S) -> UsedByCtrl = create_mods_list_ctrl(Panel, Main, - "Modules used by others", + "Modules using this", " and their applications"), wxSizer:add(Main, wxStaticLine:new(Panel, [{style, ?wxLI_VERTICAL}]), -- cgit v1.2.3