diff options
Diffstat (limited to 'lib/reltool/src')
-rw-r--r-- | lib/reltool/src/Makefile | 54 | ||||
-rw-r--r-- | lib/reltool/src/files.mk | 32 | ||||
-rw-r--r-- | lib/reltool/src/reltool.app.src | 45 | ||||
-rw-r--r-- | lib/reltool/src/reltool.appup.src | 22 | ||||
-rw-r--r-- | lib/reltool/src/reltool.erl | 248 | ||||
-rw-r--r-- | lib/reltool/src/reltool.hrl | 379 | ||||
-rw-r--r-- | lib/reltool/src/reltool_app_win.erl | 248 | ||||
-rw-r--r-- | lib/reltool/src/reltool_fgraph.erl | 34 | ||||
-rw-r--r-- | lib/reltool/src/reltool_fgraph_win.erl | 277 | ||||
-rw-r--r-- | lib/reltool/src/reltool_mod_win.erl | 251 | ||||
-rw-r--r-- | lib/reltool/src/reltool_server.erl | 1191 | ||||
-rw-r--r-- | lib/reltool/src/reltool_sys_win.erl | 391 | ||||
-rw-r--r-- | lib/reltool/src/reltool_target.erl | 803 | ||||
-rw-r--r-- | lib/reltool/src/reltool_utils.erl | 143 |
14 files changed, 2625 insertions, 1493 deletions
diff --git a/lib/reltool/src/Makefile b/lib/reltool/src/Makefile index fa24efbb8c..4e6a112b7e 100644 --- a/lib/reltool/src/Makefile +++ b/lib/reltool/src/Makefile @@ -1,19 +1,19 @@ # # %CopyrightBegin% -# -# Copyright Ericsson AB 2009. All Rights Reserved. -# +# +# Copyright Ericsson AB 2009-2010. 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% include $(ERL_TOP)/make/target.mk @@ -28,7 +28,6 @@ include ../vsn.mk VSN = $(RELTOOL_VSN) APP_VSN = "reltool-$(VSN)" - # ---------------------------------------------------- # Release directory specification # ---------------------------------------------------- @@ -39,25 +38,20 @@ RELSYSDIR = $(RELEASE_PATH)/lib/reltool-$(VSN) # Target Specs # ---------------------------------------------------- -MODULES = \ - reltool \ - reltool_app_win \ - reltool_fgraph \ - reltool_fgraph_win \ - reltool_mod_win \ - reltool_sys_win \ - reltool_server \ - reltool_target \ - reltool_utils - -HRL_FILES = - -INTERNAL_HRL_FILES = reltool.hrl reltool_fgraph.hrl +include files.mk ERL_FILES = $(MODULES:%=%.erl) TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) +APP_FILE = reltool.app +APP_SRC = $(APP_FILE).src +APP_TARGET = $(EBIN)/$(APP_FILE) + +APPUP_FILE = reltool.appup +APPUP_SRC = $(APPUP_FILE).src +APPUP_TARGET = $(EBIN)/$(APPUP_FILE) + # ---------------------------------------------------- # FLAGS # ---------------------------------------------------- @@ -69,15 +63,28 @@ ERL_COMPILE_FLAGS += +'{parse_transform,sys_pre_attributes}' \ # Targets # ---------------------------------------------------- -debug opt: $(TARGET_FILES) $(HRL_FILES) +debug: + @${MAKE} TYPE=debug opt + +opt: $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) clean: - rm -f $(TARGET_FILES) + rm -f $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) rm -f core docs: # ---------------------------------------------------- +# Special Build Targets +# ---------------------------------------------------- + +$(APP_TARGET): $(APP_SRC) ../vsn.mk + sed -e 's;%VSN%;$(VSN);' $< > $@ + +$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk + sed -e 's;%VSN%;$(VSN);' $< > $@ + +# ---------------------------------------------------- # Dependencies # ---------------------------------------------------- @@ -85,7 +92,7 @@ $(TARGET_FILES): $(HRL_FILES) $(INTERNAL_HRL_FILES) # ---------------------------------------------------- # Release Target -# ---------------------------------------------------- +# ---------------------------------------------------- include $(ERL_TOP)/make/otp_release_targets.mk @@ -94,6 +101,7 @@ release_spec: opt $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src $(INSTALL_DIR) $(RELSYSDIR)/ebin $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DATA) $(APP_TARGET) $(APPUP_TARGET) $(RELSYSDIR)/ebin release_docs_spec: diff --git a/lib/reltool/src/files.mk b/lib/reltool/src/files.mk new file mode 100644 index 0000000000..99a1f1c14a --- /dev/null +++ b/lib/reltool/src/files.mk @@ -0,0 +1,32 @@ +#-*-makefile-*- ; force emacs to enter makefile-mode +# %CopyrightBegin% +# +# Copyright Ericsson AB 2010. 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% + +MODULES = \ + reltool \ + reltool_app_win \ + reltool_fgraph \ + reltool_fgraph_win \ + reltool_mod_win \ + reltool_sys_win \ + reltool_server \ + reltool_target \ + reltool_utils + +HRL_FILES = + +INTERNAL_HRL_FILES = reltool.hrl reltool_fgraph.hrl diff --git a/lib/reltool/src/reltool.app.src b/lib/reltool/src/reltool.app.src index f83042c157..4188f341f1 100644 --- a/lib/reltool/src/reltool.app.src +++ b/lib/reltool/src/reltool.app.src @@ -1,37 +1,38 @@ %% This is an -*- erlang -*- file. %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2009-2011. 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% %% +%% %CopyrightEnd% {application, reltool, - [{description, "Release management tool"}, - {vsn, "%VSN%"}, - {modules, [ - reltool, - reltool_app, - reltool_fgraph, - reltool_fgraph_win, - reltool_gen, - reltool_mod, - reltool_sys, - reltool_server, - reltool_utils - ]}, - {applications, [kernel, stdlib]} - ] -}. + [{description, "Reltool the release management tool"}, + {vsn, "%VSN%"}, + {modules, + [ + reltool, + reltool_app_win, + reltool_fgraph, + reltool_fgraph_win, + reltool_mod_win, + reltool_server, + reltool_sys_win, + reltool_target, + reltool_utils + ]}, + {registered, []}, + {applications, [stdlib, kernel]}, + {env, []} + ]}. diff --git a/lib/reltool/src/reltool.appup.src b/lib/reltool/src/reltool.appup.src new file mode 100644 index 0000000000..c02edd2afb --- /dev/null +++ b/lib/reltool/src/reltool.appup.src @@ -0,0 +1,22 @@ +%% This is an -*- erlang -*- file. +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2010. 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% + +{"%VSN%", + [ ] +}. diff --git a/lib/reltool/src/reltool.erl b/lib/reltool/src/reltool.erl index e6548bfe68..54eb1ca9e1 100644 --- a/lib/reltool/src/reltool.erl +++ b/lib/reltool/src/reltool.erl @@ -1,170 +1,66 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2009-2011. 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). %% Public -export([ - main/1, % Escript start/0, start/1, start_link/1, debug/0, % GUI - start_server/1, get_server/1, stop/1, + start_server/1, get_server/1, get_status/1, stop/1, get_config/1, get_config/3, get_rel/2, get_script/2, - create_target/2, get_target_spec/1, eval_target_spec/3, + create_target/2, get_target_spec/1, eval_target_spec/3, install/2 ]). --type file() :: string(). --type dir() :: string(). --type mod_cond() :: all | app | ebin | derived | none. --type incl_cond() :: include | exclude | derived. --type debug_info() :: keep | strip. --type app_file() :: keep | strip | all. --type re_regexp() :: string(). --type regexps() :: [re_regexp()] | {add, [re_regexp()]} | {del, [re_regexp()]} . --type incl_sys_filters() :: regexps(). --type excl_sys_filters() :: regexps(). --type incl_app_filters() :: regexps(). --type excl_app_filters() :: regexps(). --type incl_archive_filters() :: regexps(). --type excl_archive_filters() :: regexps(). --type archive_opt() :: term(). --type root_dir() :: dir(). --type lib_dir() :: dir(). --type profile() :: development | embedded | standalone. --type relocatable() :: boolean(). --type escript_file() :: file(). --type mod_name() :: atom(). --type app_name() :: atom(). --type app_vsn() :: string(). --type app_type() :: permanent | transient | temporary | load | none. --type incl_app() :: app_name(). --type rel_name() :: string(). --type rel_vsn() :: string(). --type boot_rel() :: rel_name(). --type rel_app() :: app_name() - | {app_name(), app_type()} - | {app_name(), [incl_app()]} - | {app_name(), app_type(), [incl_app()]}. --type mod() :: {incl_cond, incl_cond()} - | {debug_info, debug_info()}. --type app() :: {vsn, app_vsn()} - | {mod, mod_name(), mod()} - | {mod_cond, mod_cond()} - | {incl_cond, incl_cond()} - | {app_file, app_file()} - | {debug_info, debug_info()} - | {incl_app_filters, incl_app_filters()} - | {excl_app_filters, excl_app_filters()} - | {incl_archive_filters, incl_archive_filters()} - | {excl_archive_filters, excl_archive_filters()}. --type escript() :: {incl_cond, incl_cond()}. --type sys() :: {mod_cond, mod_cond()} - | {incl_cond, incl_cond()} - | {debug_info, debug_info()} - | {app_file, app_file()} - | {profile, profile()} - | {incl_sys_filters, incl_sys_filters()} - | {excl_sys_filters, excl_sys_filters()} - | {incl_app_filters, incl_app_filters()} - | {excl_app_filters, excl_app_filters()} - | {incl_archive_filters, incl_archive_filters()} - | {excl_archive_filters, excl_archive_filters()} - | {archive_opts, [archive_opt()]} - | {root_dir, root_dir()} - | {lib_dirs, [lib_dir()]} - | {boot_rel, boot_rel()} - | {rel, rel_name(), rel_vsn(), [rel_app()]} - | {relocatable, relocatable()} - | {erts, app()} - | {escript, escript_file(), [escript()]} - | {app, app_name(), [app()]}. --type config() :: {sys, [sys()]}. --type option() :: {wx_debug, term()} | {trap_exit, boolean()} | config() | {config, config() | file()}. --type options() :: [option()]. --type server_pid() :: pid(). --type window_pid() :: pid(). --type server() :: server_pid() | options(). --type rel_file() :: term(). --type script_file() :: term(). --type reason() :: string(). --type escript_arg() :: string(). -%%-type base_dir() :: dir(). -%%-type base_file() :: file(). -%%-type top_dir() :: file(). -%%-type top_file() :: file(). -%%-type target_spec() :: [target_spec()] -%% | {create_dir, base_dir(), [target_spec()]} -%% | {create_dir, base_dir(), top_dir(), [target_spec()]} -%% | {archive, base_file(), [archive_opt()], [target_spec()]} -%% | {copy_file, base_file()} -%% | {copy_file, base_file(), top_file()} -%% | {write_file, base_file(), iolist()} -%% | {strip_beam_file, base_file()}. --type target_spec() :: term(). --type target_dir() :: dir(). --type incl_defaults() :: boolean(). --type incl_derived() :: boolean(). +-include("reltool.hrl"). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Main function for escript --spec main([escript_arg()]) -> ok. -main(_) -> - process_flag(trap_exit, true), - {ok, WinPid} = start_link([]), - receive - {'EXIT', WinPid, shutdown} -> - ok; - {'EXIT', WinPid, normal} -> - ok; - {'EXIT', WinPid, Reason} -> - io:format("EXIT: ~p\n", [Reason]), - erlang:halt(1) - end. - %% Start main window process --spec start() -> {ok, window_pid()}. +-spec start() -> {ok, window_pid()} | {error, reason()}. start() -> start([]). %% Start main window process --spec start(options()) -> {ok, window_pid() | {error, reason()}}. -start(Options)when is_list(Options) -> - {ok, WinPid} = start_link(Options), - unlink(WinPid), - {ok, WinPid}. +-spec start(options()) -> {ok, window_pid()} | {error, reason()}. +start(Options) when is_list(Options) -> + case start_link(Options) of + {ok, WinPid} = OK -> + unlink(WinPid), + OK; + {error, _Reason} = Error -> + Error + end. %% Start main window process with wx debugging enabled --spec debug() -> {ok, window_pid()}. +-spec debug() -> {ok, window_pid()} | {error, reason()}. debug() -> - {ok, WinPid} = start_link([{wx_debug, 2}]), - unlink(WinPid), - {ok, WinPid}. + start([{wx_debug, 2}]). %% Start main window process with options --spec start_link(options()) -> {ok, window_pid() | {error, reason()}}. +-spec start_link(options()) -> {ok, window_pid()} | {error, reason()}. start_link(Options) when is_list(Options) -> case reltool_sys_win:start_link(Options) of - {ok, WinPid} -> - {ok, WinPid}; + {ok, _WinPid} = OK -> + OK; {error, Reason} -> {error, lists:flatten(io_lib:format("~p", [Reason]))} - end. + end. %% Start server process with options -spec start_server(options()) -> {ok, server_pid()} | {error, reason()}. @@ -180,8 +76,8 @@ start_server(Options) -> -spec get_server(window_pid()) -> {ok, server_pid()} | {error, reason()}. get_server(WinPid) -> case reltool_sys_win:get_server(WinPid) of - {ok, ServerPid} -> - {ok, ServerPid}; + {ok, _ServerPid} = OK -> + OK; {error, Reason} -> {error, lists:flatten(io_lib:format("~p", [Reason]))} end. @@ -200,51 +96,93 @@ stop(Pid) when is_pid(Pid) -> end. %% Internal library function --spec eval_server(server(), fun((server_pid()) -> term())) -> {ok, server_pid()} | {error, reason()}. -eval_server(Pid, Fun) when is_pid(Pid) -> +-spec eval_server(server(), boolean(), fun((server_pid()) -> Ret)) -> + Ret | {error, reason()} when Ret :: term(). +eval_server(Pid, _DisplayWarnings, Fun) + when is_pid(Pid) -> Fun(Pid); -eval_server(Options, Fun) when is_list(Options), is_function(Fun, 1) -> - case start_server(Options) of - {ok, Pid} -> - Res = Fun(Pid), - stop(Pid), - Res; - {error, Reason} -> - {error, Reason} +eval_server(Options, DisplayWarnings, Fun) + when is_list(Options) -> + TrapExit = process_flag(trap_exit, true), + Res = case start_server(Options) of + {ok, Pid} -> + apply_fun(Pid, DisplayWarnings, Fun); + {error, _Reason} = Error -> + Error + end, + process_flag(trap_exit, TrapExit), + Res. + +apply_fun(Pid, false, Fun) -> + Res = Fun(Pid), + stop(Pid), + Res; +apply_fun(Pid, true, Fun) -> + case get_status(Pid) of + {ok, Warnings} -> + [io:format("~p: ~s\n", [?APPLICATION, W]) || W <- Warnings], + apply_fun(Pid, false, Fun); + {error, _Reason} = Error -> + stop(Pid), + Error end. - + +%% Get status about the configuration +-type warning() :: string(). +-spec get_status(server()) -> {ok, [warning()]} | {error, reason()}. +get_status(PidOrOptions) + when is_pid(PidOrOptions); is_list(PidOrOptions) -> + eval_server(PidOrOptions, false, + fun(Pid) -> reltool_server:get_status(Pid) end). + %% Get reltool configuration -spec get_config(server()) -> {ok, config()} | {error, reason()}. get_config(PidOrOption) -> get_config(PidOrOption, false, false). --spec get_config(server(), incl_defaults(), incl_derived()) -> {ok, config()} | {error, reason()}. -get_config(PidOrOptions, InclDefaults, InclDerived) when is_pid(PidOrOptions); is_list(PidOrOptions) -> - eval_server(PidOrOptions, fun(Pid) -> reltool_server:get_config(Pid, InclDefaults, InclDerived) end). +-spec get_config(server(), incl_defaults(), incl_derived()) -> + {ok, config()} | {error, reason()}. +get_config(PidOrOptions, InclDef, InclDeriv) + when is_pid(PidOrOptions); is_list(PidOrOptions) -> + eval_server(PidOrOptions, true, + fun(Pid) -> + reltool_server:get_config(Pid, InclDef, InclDeriv) + end). %% Get contents of release file -spec get_rel(server(), rel_name()) -> {ok, rel_file()} | {error, reason()}. -get_rel(PidOrOptions, RelName) when is_pid(PidOrOptions); is_list(PidOrOptions) -> - eval_server(PidOrOptions, fun(Pid) -> reltool_server:get_rel(Pid, RelName) end). +get_rel(PidOrOptions, RelName) + when is_pid(PidOrOptions); is_list(PidOrOptions) -> + eval_server(PidOrOptions, true, + fun(Pid) -> reltool_server:get_rel(Pid, RelName) end). %% Get contents of boot script file --spec get_script(server(), rel_name()) -> {ok, script_file()} | {error, reason()}. -get_script(PidOrOptions, RelName) when is_pid(PidOrOptions); is_list(PidOrOptions) -> - eval_server(PidOrOptions, fun(Pid) -> reltool_server:get_script(Pid, RelName) end). +-spec get_script(server(), rel_name()) -> + {ok, script_file()} | {error, reason()}. +get_script(PidOrOptions, RelName) + when is_pid(PidOrOptions); is_list(PidOrOptions) -> + eval_server(PidOrOptions, true, + fun(Pid) -> reltool_server:get_script(Pid, RelName) end). %% Generate a target system -spec create_target(server(), target_dir()) -> ok | {error, reason()}. -create_target(PidOrOptions, TargetDir) when is_pid(PidOrOptions); is_list(PidOrOptions) -> - eval_server(PidOrOptions, fun(Pid) -> reltool_server:gen_target(Pid, TargetDir) end). +create_target(PidOrOptions, TargetDir) + when is_pid(PidOrOptions); is_list(PidOrOptions) -> + eval_server(PidOrOptions, true, + fun(Pid) -> reltool_server:gen_target(Pid, TargetDir) end). %% Generate a target system -spec get_target_spec(server()) -> {ok, target_spec()} | {error, reason()}. -get_target_spec(PidOrOptions) when is_pid(PidOrOptions); is_list(PidOrOptions) -> - eval_server(PidOrOptions, fun(Pid) -> reltool_server:gen_spec(Pid) end). +get_target_spec(PidOrOptions) + when is_pid(PidOrOptions); is_list(PidOrOptions) -> + eval_server(PidOrOptions, true, + fun(Pid) -> reltool_server:gen_spec(Pid) end). %% Generate a target system --spec eval_target_spec(target_spec(), root_dir(), target_dir()) -> ok | {error, reason()}. -eval_target_spec(Spec, SourceDir, TargetDir) when is_list(SourceDir), is_list(TargetDir) -> +-spec eval_target_spec(target_spec(), root_dir(), target_dir()) -> + ok | {error, reason()}. +eval_target_spec(Spec, SourceDir, TargetDir) + when is_list(SourceDir), is_list(TargetDir) -> reltool_target:eval_spec(Spec, SourceDir, TargetDir). %% Install a target system diff --git a/lib/reltool/src/reltool.hrl b/lib/reltool/src/reltool.hrl index 736daab0f0..93f47f6381 100644 --- a/lib/reltool/src/reltool.hrl +++ b/lib/reltool/src/reltool.hrl @@ -1,152 +1,251 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2009-2011. 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% --define(APPLICATION, reltool). --define(MISSING_APP, '*MISSING*'). +-define(APPLICATION, reltool). +-define(MISSING_APP_NAME, '*MISSING*'). -define(MISSING_APP_TEXT, "*MISSING*"). --record(common, - { - sys_debug, % term() - wx_debug, % term() - trap_exit, % bool() - app_tab, % ets_tab() - mod_tab, % ets_tab() - mod_used_by_tab % ets_tab() - }). +-type file() :: file:filename(). +-type dir() :: file:filename(). +%% app - Include all modules in app file +%% ebin - Include all modules on ebin directory +%% derived - Include only those modules that others are dependent on +-type mod_cond() :: all | app | ebin | derived | none. +-type incl_cond() :: include | exclude | derived. +-type debug_info() :: keep | strip. +-type app_file() :: keep | strip | all. +-type re_regexp() :: string(). % re:regexp() +-type regexps() :: [re_regexp()] | + {add, [re_regexp()]} | + {del, [re_regexp()]} . +-type incl_sys_filters() :: regexps(). +-type excl_sys_filters() :: regexps(). +-type incl_app_filters() :: regexps(). +-type excl_app_filters() :: regexps(). +-type incl_archive_filters() :: regexps(). +-type excl_archive_filters() :: regexps(). +-type archive_opt() :: term(). % zip:create() +-type root_dir() :: dir(). +-type lib_dir() :: dir(). +-type profile() :: development | embedded | standalone. +-type relocatable() :: boolean(). +-type escript_file() :: file(). +-type mod_name() :: atom(). +-type app_name() :: atom(). +-type app_vsn() :: string(). % e.g. "4.7" +-type app_label() :: string(). % e.g. "mnesia" or "mnesia-4.7" +-type app_type() :: permanent | transient | temporary | load | none. +-type incl_app() :: app_name(). +-type emu_name() :: string(). +-type rel_name() :: string(). +-type rel_vsn() :: string(). +-type boot_rel() :: rel_name(). +-type rel_app() :: app_name() + | {app_name(), app_type()} + | {app_name(), [incl_app()]} + | {app_name(), app_type(), [incl_app()]}. +-type mod() :: {incl_cond, incl_cond()} + | {debug_info, debug_info()}. +-type app() :: {vsn, app_vsn()} + | {mod, mod_name(), mod()} + | {mod_cond, mod_cond()} + | {incl_cond, incl_cond()} + | {app_file, app_file()} + | {debug_info, debug_info()} + | {incl_app_filters, incl_app_filters()} + | {excl_app_filters, excl_app_filters()} + | {incl_archive_filters, incl_archive_filters()} + | {excl_archive_filters, excl_archive_filters()}. +-type escript() :: {incl_cond, incl_cond()}. +-type sys() :: {mod_cond, mod_cond()} + | {incl_cond, incl_cond()} + | {debug_info, debug_info()} + | {app_file, app_file()} + | {profile, profile()} + | {incl_sys_filters, incl_sys_filters()} + | {excl_sys_filters, excl_sys_filters()} + | {incl_app_filters, incl_app_filters()} + | {excl_app_filters, excl_app_filters()} + | {incl_archive_filters, incl_archive_filters()} + | {excl_archive_filters, excl_archive_filters()} + | {archive_opts, [archive_opt()]} + | {root_dir, root_dir()} + | {lib_dirs, [lib_dir()]} + | {boot_rel, boot_rel()} + | {rel, rel_name(), rel_vsn(), [rel_app()]} + | {relocatable, relocatable()} + | {erts, app()} + | {escript, escript_file(), [escript()]} + | {app, app_name(), [app()]}. +-type config() :: {sys, [sys()]}. +-type option() :: {wx_debug, term()} + | {trap_exit, boolean()} + | config() + | {config, config() | file()}. +-type options() :: [option()]. +-type server_pid() :: pid(). +-type window_pid() :: pid(). +-type server() :: server_pid() | options(). +-type rel_file() :: term(). +-type script_file() :: term(). +-type reason() :: string(). --record(sys, +-type base_dir() :: dir(). +-type base_file() :: file(). +-type top_dir() :: file(). +-type top_file() :: file(). +-type target_spec() :: [target_spec()] + | {create_dir, base_dir(), [target_spec()]} + | {create_dir, base_dir(), top_dir(), [target_spec()]} + | {archive, base_file(), [archive_opt()], [target_spec()]} + | {copy_file, base_file()} + | {copy_file, base_file(), top_file()} + | {write_file, base_file(), iolist()} + | {strip_beam_file, base_file()}. +-type target_dir() :: dir(). +-type incl_defaults() :: boolean(). +-type incl_derived() :: boolean(). +-type status() :: missing | ok. + +-record(common, { - %% Sources - root_dir, % directory() - lib_dirs, % [directory()] - escripts, % [file()] - mod_cond, % all | app | ebin | derived | none - incl_cond, % include | exclude | derived - apps, % [#app{}] + sys_debug :: term(), + wx_debug :: term(), + trap_exit :: boolean(), + app_tab :: ets:tab(), + mod_tab :: ets:tab(), + mod_used_by_tab :: ets:tab() + }). - %% Target cond - boot_rel, % string() - rels, % [#rel{}] - emu_name, % string() - profile, % standalone | development | embedded - incl_sys_filters, % [regexp()] - excl_sys_filters, % [regexp()] - incl_app_filters, % [regexp()] - excl_app_filters, % [regexp()] - incl_archive_filters, % [regexp()] - excl_archive_filters, % [regexp()] - archive_opts, % [zip:create()] - relocatable, % bool() - app_type, % permanent | transient | temporary | load | none - app_file, % keep | strip | all - debug_info % keep | strip - }). +-record(mod, + { %% Static + name :: mod_name(), + app_name :: app_name(), + incl_cond :: incl_cond() | undefined, + debug_info :: debug_info() | undefined, + is_app_mod :: boolean(), + is_ebin_mod :: boolean(), + uses_mods :: [mod_name()], + exists :: boolean(), + %% Dynamic + status :: status(), + used_by_mods :: [mod_name()], + is_pre_included :: boolean() | undefined, + is_included :: boolean() | undefined + }). --record(rel, +-record(app_info, { - name, % string() - vsn, % string() - rel_apps % [#rel_app{}] - }). + description = "" :: string(), + id = "" :: string(), + vsn = "" :: app_vsn(), + modules = [] :: [mod_name()], + maxP = infinity :: integer() | infinity, + maxT = infinity :: integer() | infinity, + registered = [] :: [atom()], + incl_apps = [] :: [app_name()], + applications = [] :: [app_name()], + env = [] :: [{atom(), term()}], + mod = undefined :: {mod_name(), [term()]} | undefined, + start_phases = undefined :: [{atom(), term()}] | undefined + }). --record(rel_app, - { - name, % atom() - app_type, % permanent | transient | temporary | load | none - incl_apps % [atom()] - }). +-record(regexp, {source, compiled}). -record(app, - {%% Static info - name, % atom() - is_escript, % bool() - use_selected_vsn,% bool() | undefined - active_dir, % dir_name() - sorted_dirs, % [dir_name()] - vsn, % string() e.g. "4.7" - label, % string() e.g. "mnesia" or "mnesia-4.7" - info, % #app_info{} | undefined - mods, % [#mod{}] + { %% Static info + name :: app_name(), + is_escript :: boolean(), + use_selected_vsn :: boolean() | undefined, + active_dir :: dir(), + sorted_dirs :: [dir()], + vsn :: app_vsn(), + label :: app_label(), + info :: #app_info{} | undefined, + mods :: [#mod{}], %% Static source cond - mod_cond, % all | app | ebin | derived | none | undefined - incl_cond, % include | exclude | derived | undefined + mod_cond :: mod_cond() | undefined, + incl_cond :: incl_cond() | undefined, %% Static target cond - debug_info, % keep | strip | undefined - app_file, % keep | strip | all | undefined - app_type, % permanent | transient | temporary | load | none - incl_app_filters, % [regexp()] - excl_app_filters, % [regexp()] - incl_archive_filters, % [regexp()] - excl_archive_filters, % [regexp()] - archive_opts, % [zip_create_opt()] + debug_info :: debug_info() | undefined, + app_file :: app_file() | undefined, + app_type :: app_type() | undefined, + incl_app_filters :: [#regexp{}], + excl_app_filters :: [#regexp{}], + incl_archive_filters :: [#regexp{}], + excl_archive_filters :: [#regexp{}], + archive_opts :: [archive_opt()], %% Dynamic - status, % missing | ok - uses_mods, % [atom()] - used_by_mods, % [atom()] - uses_apps, % [atom()] - used_by_apps, % [atom()] - is_pre_included, % bool() - is_included % bool() - }). - --record(mod, - {%% Static - name, % atom() - app_name, % atom() - incl_cond, % include | exclude | derived | undefined - debug_info, % keep | strip | undefined - is_app_mod, % bool(), - is_ebin_mod, % bool(), - uses_mods, % [module()] - exists, % bool() - %% Dynamic - status, % missing | ok - used_by_mods, % [atom()] - is_pre_included, % bool() | undefined - is_included % bool() | undefined - }). + status :: status(), + uses_mods :: [mod_name()], + used_by_mods :: [mod_name()], + uses_apps :: [app_name()], + used_by_apps :: [app_name()], + is_pre_included :: boolean(), + is_included :: boolean(), + rels :: [rel_name()] + }). -%% app - Include all modules in app file -%% ebin - Include all modules on ebin directory -%% derived - Include only those modules that others are dependent on +-record(rel_app, + { + name :: app_name(), + app_type :: app_type() | undefined, + incl_apps = [] :: [incl_app()] + }). --record(app_info, +-record(rel, { - description = "", - id = "", - vsn = "", - modules = [], - maxP = infinity, - maxT = infinity, - registered = [], - incl_apps = [], - applications = [], - env = [], - mod = undefined, - start_phases = undefined - }). + name :: rel_name(), + vsn :: rel_vsn(), + rel_apps :: [#rel_app{}] + }). + +-record(sys, + { %% Sources + root_dir :: dir(), + lib_dirs :: [dir()], + escripts :: [file()], + mod_cond :: mod_cond(), + incl_cond :: incl_cond(), + apps :: [#app{}], + + %% Target cond + boot_rel :: boot_rel(), + rels :: [#rel{}], + emu_name :: emu_name(), + profile :: profile(), + incl_sys_filters :: [#regexp{}], + excl_sys_filters :: [#regexp{}], + incl_app_filters :: [#regexp{}], + excl_app_filters :: [#regexp{}], + incl_archive_filters :: [#regexp{}], + excl_archive_filters :: [#regexp{}], + archive_opts :: [archive_opt()], + relocatable :: boolean(), + rel_app_type :: app_type(), + embedded_app_type :: app_type() | undefined, + app_file :: app_file(), + debug_info :: debug_info() + }). --record(regexp, {source, compiled}). - -define(ERR_IMAGE, 0). -define(WARN_IMAGE, 1). -define(QUEST_IMAGE, 2). @@ -165,38 +264,42 @@ -define(DEFAULT_EMU_NAME, "beam"). -define(DEFAULT_PROFILE, development). -define(DEFAULT_RELOCATABLE, true). --define(DEFAULT_APP_TYPE, permanent). +-define(DEFAULT_REL_APP_TYPE, permanent). +-define(DEFAULT_EMBEDDED_APP_TYPE, undefined). -define(DEFAULT_APP_FILE, keep). -define(DEFAULT_DEBUG_INFO, keep). -define(DEFAULT_INCL_ARCHIVE_FILTERS, [".*"]). --define(DEFAULT_EXCL_ARCHIVE_FILTERS, ["^include$", "^priv$"]). --define(DEFAULT_ARCHIVE_OPTS, []). +-define(DEFAULT_EXCL_ARCHIVE_FILTERS, ["^include\$", "^priv\$"]). +-define(DEFAULT_ARCHIVE_OPTS, []). -define(DEFAULT_INCL_SYS_FILTERS, [".*"]). -define(DEFAULT_EXCL_SYS_FILTERS, []). -define(DEFAULT_INCL_APP_FILTERS, [".*"]). -define(DEFAULT_EXCL_APP_FILTERS, []). --define(EMBEDDED_INCL_SYS_FILTERS, ["^bin", - "^erts", - "^lib", - "^releases"]). --define(EMBEDDED_EXCL_SYS_FILTERS, ["^bin/(erlc|dialyzer|typer)(|\\.exe)$", - "^erts.*/bin/(erlc|dialyzer|typer)(|\\.exe)$", - "^erts.*/bin/.*(debug|pdb)"]). +-define(EMBEDDED_INCL_SYS_FILTERS, ["^bin", + "^erts", + "^lib", + "^releases"]). +-define(EMBEDDED_EXCL_SYS_FILTERS, + ["^bin/(erlc|dialyzer|typer)(|\\.exe)\$", + "^erts.*/bin/(erlc|dialyzer|typer)(|\\.exe)\$", + "^erts.*/bin/.*(debug|pdb)"]). -define(EMBEDDED_INCL_APP_FILTERS, ["^ebin", - "^priv", - "^include"]). + "^include", + "^priv"]). -define(EMBEDDED_EXCL_APP_FILTERS, []). +-define(EMBEDDED_APP_TYPE, load). --define(STANDALONE_INCL_SYS_FILTERS, ["^bin/(erl|epmd)(|\\.exe|\\.ini)$", - "^bin/start(|_clean).boot$", - "^erts.*/bin", - "^lib$"]). --define(STANDALONE_EXCL_SYS_FILTERS, ["^erts.*/bin/(erlc|dialyzer|typer)(|\\.exe)$", - "^erts.*/bin/(start|escript|to_erl|run_erl)(|\\.exe)$", - "^erts.*/bin/.*(debug|pdb)"]). --define(STANDALONE_INCL_APP_FILTERS, ["^ebin", - "^priv"]). --define(STANDALONE_EXCL_APP_FILTERS, ["^ebin/.*\\.appup$"]). +-define(STANDALONE_INCL_SYS_FILTERS, ["^bin/(erl|epmd)(|\\.exe|\\.ini)\$", + "^bin/start(|_clean).boot\$", + "^erts.*/bin", + "^lib\$"]). +-define(STANDALONE_EXCL_SYS_FILTERS, + ["^erts.*/bin/(erlc|dialyzer|typer)(|\\.exe)\$", + "^erts.*/bin/(start|escript|to_erl|run_erl)(|\\.exe)\$", + "^erts.*/bin/.*(debug|pdb)"]). +-define(STANDALONE_INCL_APP_FILTERS, ["^ebin", + "^priv"]). +-define(STANDALONE_EXCL_APP_FILTERS, ["^ebin/.*\\.appup\$"]). diff --git a/lib/reltool/src/reltool_app_win.erl b/lib/reltool/src/reltool_app_win.erl index 6083493c02..70bd72b258 100644 --- a/lib/reltool/src/reltool_app_win.erl +++ b/lib/reltool/src/reltool_app_win.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2009-2010. 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_app_win). @@ -34,7 +34,7 @@ -include_lib("wx/include/wx.hrl"). -include("reltool.hrl"). --record(state, +-record(state, {parent_pid, xref_pid, mod_wins, @@ -63,7 +63,7 @@ %% -define(APPS_APP_COL_WIDTH, 250). -define(CLOSE_ITEM, ?wxID_EXIT). %% Use OS specific version if available --define(ABOUT_ITEM, ?wxID_ABOUT). %% Use OS specific +-define(ABOUT_ITEM, ?wxID_ABOUT). %% Use OS specific -define(CONTENTS_ITEM, 300). -define(MODS_MOD_COL, 0). @@ -79,7 +79,11 @@ %% Client start_link(WxEnv, Xref, Common, AppName) -> - proc_lib:start_link(?MODULE, init, [self(), WxEnv, Xref, Common, AppName], infinity, []). + proc_lib:start_link(?MODULE, + init, + [self(), WxEnv, Xref, Common, AppName], + infinity, + []). raise(Pid) -> reltool_utils:cast(Pid, raise). @@ -121,7 +125,12 @@ loop(#state{xref_pid = Xref, common = C, app = App} = S) -> receive {system, From, Msg} -> Dbg = C#common.sys_debug, - sys:handle_system_msg(Msg, From, S#state.parent_pid, ?MODULE, Dbg, S); + sys:handle_system_msg(Msg, + From, + S#state.parent_pid, + ?MODULE, + Dbg, + S); {cast, _From, raise} -> wxFrame:raise(S#state.frame), wxFrame:setFocus(S#state.frame), @@ -131,7 +140,8 @@ loop(#state{xref_pid = Xref, common = C, app = App} = S) -> {ok, App2} -> {ok, Sys} = reltool_server:get_sys(Xref), S2 = redraw_window(S#state{sys = Sys, app = App2}), - [ok = reltool_mod_win:refresh(MW#mod_win.pid) || MW <- S2#state.mod_wins], + [ok = reltool_mod_win:refresh(MW#mod_win.pid) || + MW <- S2#state.mod_wins], ?MODULE:loop(S2); {error, _Reason} -> wxFrame:destroy(S#state.frame), @@ -139,7 +149,8 @@ loop(#state{xref_pid = Xref, common = C, app = App} = S) -> end; {call, ReplyTo, Ref, {open_mod, ModName}} -> S2 = create_mod_window(S, ModName), - {value, #mod_win{pid = ModPid}} = lists:keysearch(ModName, #mod_win.name, S2#state.mod_wins), + {value, #mod_win{pid = ModPid}} = + lists:keysearch(ModName, #mod_win.name, S2#state.mod_wins), reltool_utils:reply(ReplyTo, Ref, {ok, ModPid}), ?MODULE:loop(S2); #wx{event = #wxSize{}} = Wx -> @@ -157,7 +168,9 @@ loop(#state{xref_pid = Xref, common = C, app = App} = S) -> exit(Reason); {'EXIT', Pid, _Reason} = Exit -> exit_warning(Exit), - S2 = S#state{mod_wins = lists:keydelete(Pid, #mod_win.pid, S#state.mod_wins)}, + S2 = S#state{mod_wins = lists:keydelete(Pid, + #mod_win.pid, + S#state.mod_wins)}, ?MODULE:loop(S2); Msg -> error_logger:format("~p~p got unexpected message:\n\t~p\n", @@ -179,7 +192,7 @@ create_window(#state{app = App} = S) -> StatusBar = wxFrame:createStatusBar(Frame,[]), Book = wxNotebook:new(Panel, ?wxID_ANY, []), - + S2 = S#state{frame = Frame, panel = Panel, book = Book, @@ -210,12 +223,16 @@ create_apps_page(S, Derived) -> Lower = wxBoxSizer:new(?wxHORIZONTAL), UsedByCtrl = create_apps_list_ctrl(Panel, Upper, "Used by"), - wxSizer:add(Upper, wxStaticLine:new(Panel, [{style, ?wxLI_VERTICAL}]), [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}]), - + wxSizer:add(Upper, + wxStaticLine:new(Panel, [{style, ?wxLI_VERTICAL}]), + [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}]), + RequiredCtrl = create_apps_list_ctrl(Panel, Upper, "Required"), - wxSizer:add(Upper, wxStaticLine:new(Panel, [{style, ?wxLI_VERTICAL}]), [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}]), + wxSizer:add(Upper, wxStaticLine:new(Panel, [{style, ?wxLI_VERTICAL}]), + [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}]), InclCtrl = create_apps_list_ctrl(Panel, Upper, "Included"), - wxSizer:add(Upper, wxStaticLine:new(Panel, [{style, ?wxLI_VERTICAL}]), [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}]), + wxSizer:add(Upper, wxStaticLine:new(Panel, [{style, ?wxLI_VERTICAL}]), + [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}]), UsesCtrl = create_apps_list_ctrl(Panel, Upper, "Uses"), S2 = S#state{app_required_ctrl = RequiredCtrl, app_used_by_ctrl = UsedByCtrl, @@ -262,8 +279,10 @@ create_apps_list_ctrl(Panel, Sizer, Text) -> [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}, {proportion, 1}]), - wxEvtHandler:connect(ListCtrl, size, [{skip, true}, {userData, apps_list_ctrl}]), - wxListCtrl:connect(ListCtrl, command_list_item_activated, [{userData, open_app}]), + wxEvtHandler:connect(ListCtrl, size, + [{skip, true}, {userData, apps_list_ctrl}]), + wxListCtrl:connect(ListCtrl, command_list_item_activated, + [{userData, open_app}]), wxWindow:connect(ListCtrl, enter_window), ListCtrl. @@ -271,9 +290,20 @@ create_deps_page(S, Derived) -> Panel = wxPanel:new(S#state.book, []), Main = wxBoxSizer:new(?wxHORIZONTAL), - UsedByCtrl = create_mods_list_ctrl(Panel, Main, "Modules used by others", " and their applications", undefined, undefined), - wxSizer:add(Main, wxStaticLine:new(Panel, [{style, ?wxLI_VERTICAL}]), [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}]), - UsesCtrl = create_mods_list_ctrl(Panel, Main, "Used modules", " and their applications", undefined, undefined), + UsedByCtrl = create_mods_list_ctrl(Panel, + Main, + "Modules used by others", + " and their applications", + undefined, + undefined), + wxSizer:add(Main, wxStaticLine:new(Panel, [{style, ?wxLI_VERTICAL}]), + [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}]), + UsesCtrl = create_mods_list_ctrl(Panel, + Main, + "Used modules", + " and their applications", + undefined, + undefined), S2 = S#state{deps_used_by_ctrl = UsedByCtrl, deps_uses_ctrl = UsesCtrl}, redraw_mods(S2, Derived), @@ -285,13 +315,36 @@ create_mods_page(S, Derived) -> Panel = wxPanel:new(S#state.book, []), MainSz = wxBoxSizer:new(?wxHORIZONTAL), - SourceCtrl = create_mods_list_ctrl(Panel, MainSz, ?source, "", whitelist_add, blacklist_add), - wxSizer:add(MainSz, wxStaticLine:new(Panel, [{style, ?wxLI_VERTICAL}]), [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}]), - WhiteCtrl = create_mods_list_ctrl(Panel, MainSz, ?whitelist, "", whitelist_del, blacklist_add), - wxSizer:add(MainSz, wxStaticLine:new(Panel, [{style, ?wxLI_VERTICAL}]), [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}]), - BlackCtrl = create_mods_list_ctrl(Panel, MainSz, ?blacklist, "", whitelist_add, blacklist_del), - wxSizer:add(MainSz, wxStaticLine:new(Panel, [{style, ?wxLI_VERTICAL}]), [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}]), - DerivedCtrl = create_mods_list_ctrl(Panel, MainSz, ?derived, "", whitelist_add, blacklist_add), + SourceCtrl = create_mods_list_ctrl(Panel, + MainSz, + ?source, + "", + whitelist_add, + blacklist_add), + wxSizer:add(MainSz, wxStaticLine:new(Panel, [{style, ?wxLI_VERTICAL}]), + [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}]), + WhiteCtrl = create_mods_list_ctrl(Panel, + MainSz, + ?whitelist, + "", + whitelist_del, + blacklist_add), + wxSizer:add(MainSz, wxStaticLine:new(Panel, [{style, ?wxLI_VERTICAL}]), + [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}]), + BlackCtrl = create_mods_list_ctrl(Panel, + MainSz, + ?blacklist, + "", + whitelist_add, + blacklist_del), + wxSizer:add(MainSz, wxStaticLine:new(Panel, [{style, ?wxLI_VERTICAL}]), + [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}]), + DerivedCtrl = create_mods_list_ctrl(Panel, + MainSz, + ?derived, + "", + whitelist_add, + blacklist_add), S2 = S#state{mods_source_ctrl = SourceCtrl, mods_white_ctrl = WhiteCtrl, mods_black_ctrl = BlackCtrl, @@ -309,7 +362,8 @@ create_mods_list_ctrl(Panel, OuterSz, Title, AppText, Tick, Cross) -> %% ?wxLC_SINGLE_SEL bor ?wxHSCROLL bor ?wxVSCROLL}]), - ToolTip = "Select module(s) or open separate module window with a double click.", + ToolTip = "Select module(s) or open separate module " + "window with a double click.", wxListCtrl:setToolTip(ListCtrl, ToolTip), %% Prep images @@ -326,7 +380,8 @@ create_mods_list_ctrl(Panel, OuterSz, Title, AppText, Tick, Cross) -> true -> wxListItem:setText(ListItem, AppText), wxListCtrl:insertColumn(ListCtrl, ?MODS_APP_COL, ListItem), - %% wxListCtrl:setColumnWidth(ListCtrl, ?MODS_APP_COL, ?MODS_APP_COL_WIDTH), + %% wxListCtrl:setColumnWidth(ListCtrl, ?MODS_APP_COL, + %% ?MODS_APP_COL_WIDTH), 2; false -> 1 @@ -336,9 +391,11 @@ create_mods_list_ctrl(Panel, OuterSz, Title, AppText, Tick, Cross) -> ButtonSz = wxBoxSizer:new(?wxHORIZONTAL), create_button(Panel, ButtonSz, ListCtrl, Title, "wxART_TICK_MARK", Tick), create_button(Panel, ButtonSz, ListCtrl, Title, "wxART_CROSS_MARK", Cross), - wxEvtHandler:connect(ListCtrl, size, [{skip, true}, {userData, mods_list_ctrl}]), - wxListCtrl:connect(ListCtrl, command_list_item_activated, [{userData, open_mod}]), - wxWindow:connect(ListCtrl, enter_window), + wxEvtHandler:connect(ListCtrl, size, + [{skip, true}, {userData, mods_list_ctrl}]), + wxListCtrl:connect(ListCtrl, command_list_item_activated, + [{userData, open_mod}]), + wxWindow:connect(ListCtrl, enter_window), InnerSz = wxBoxSizer:new(?wxVERTICAL), wxSizer:add(InnerSz, ListCtrl, [{border, 2}, @@ -377,7 +434,7 @@ action_to_tool_tip(Label, Action) -> "Remove selected module(s)from whitelist."; blacklist_add when Label =:= ?blacklist -> "Remove selected module(s) from blacklist."; - blacklist_add -> + blacklist_add -> "Add selected module(s) to blacklist."; blacklist_del -> "Remove selected module(s) from blacklist." @@ -444,8 +501,8 @@ create_config_page(#state{app = App} = S) -> wxNotebook:addPage(S2#state.book, Panel, "Application settings", []), S2. -create_double_box(Panel, Sizer, TopLabel, - OuterText, OuterData, +create_double_box(Panel, Sizer, TopLabel, + OuterText, OuterData, InnerText, InnerData, InternalLabel, InternalChoices, InternalChoiceData) -> TopSizer = wxStaticBoxSizer:new(?wxVERTICAL, Panel, @@ -457,10 +514,10 @@ create_double_box(Panel, Sizer, TopLabel, [{userData, OuterData}]), InnerRadio = wxRadioButton:new(Panel, ?wxID_ANY, InnerText), wxEvtHandler:connect(InnerRadio, command_radiobutton_selected, - [{userData, InnerData}]), - InnerBox = wxRadioBox:new(Panel, + [{userData, InnerData}]), + InnerBox = wxRadioBox:new(Panel, ?wxID_ANY, - InternalLabel, + InternalLabel, ?wxDefaultPosition, ?wxDefaultSize, InternalChoices, @@ -487,29 +544,38 @@ handle_event(#state{sys = Sys, app = App} = S, Wx) -> #wx{obj = ObjRef, event = #wxMouse{type = enter_window}} -> wxWindow:setFocus(ObjRef), S; - #wx{obj= ListCtrl, userData = mods_list_ctrl, event = #wxSize{type = size, size = {W, _H}}} -> + #wx{obj= ListCtrl, + userData = mods_list_ctrl, + event = #wxSize{type = size, size = {W, _H}}} -> HasApps = (wxListCtrl:getColumnCount(ListCtrl) > 1), case HasApps of false -> wxListCtrl:setColumnWidth(ListCtrl, ?MODS_MOD_COL, W); true -> - wxListCtrl:setColumnWidth(ListCtrl, ?MODS_MOD_COL, (2 * W) div 3), + wxListCtrl:setColumnWidth(ListCtrl, + ?MODS_MOD_COL, + (2 * W) div 3), wxListCtrl:setColumnWidth(ListCtrl, ?MODS_APP_COL, W div 3) end, S; - #wx{obj= ListCtrl, userData = apps_list_ctrl, event = #wxSize{type = size, size = {W, _H}}} -> + #wx{obj = ListCtrl, + userData = apps_list_ctrl, + event = #wxSize{type = size, size = {W, _H}}} -> wxListCtrl:setColumnWidth(ListCtrl, ?APPS_APP_COL, W), S; #wx{userData = open_app, obj = ListCtrl, - event = #wxList{type = command_list_item_activated, itemIndex = Pos}} -> + event = #wxList{type = command_list_item_activated, + itemIndex = Pos}} -> AppBase = wxListCtrl:getItemText(ListCtrl, Pos), {AppName, _AppVsn} = reltool_utils:split_app_name(AppBase), - {ok, _AppPid} = reltool_sys_win:open_app(S#state.parent_pid, AppName), + {ok, _AppPid} = reltool_sys_win:open_app(S#state.parent_pid, + AppName), S; #wx{userData = open_mod, obj = ListCtrl, - event = #wxList{type = command_list_item_activated, itemIndex = Pos}} -> + event = #wxList{type = command_list_item_activated, + itemIndex = Pos}} -> ModName = list_to_atom(wxListCtrl:getItemText(ListCtrl, Pos)), create_mod_window(S, ModName); #wx{userData = global_incl_cond} -> @@ -560,16 +626,19 @@ handle_event(#state{sys = Sys, app = App} = S, Wx) -> Items = reltool_utils:get_items(ListCtrl), handle_mod_button(S, Items, Action); _ -> - error_logger:format("~p~p got unexpected app event from wx:\n\t~p\n", + error_logger:format("~p~p got unexpected app event from " + "wx:\n\t~p\n", [?MODULE, self(), Wx]), S end. -create_mod_window(#state{parent_pid = RelPid, xref_pid = Xref, common = C} = S, ModName) -> +create_mod_window(#state{parent_pid = RelPid, xref_pid = Xref, common = C} = S, + ModName) -> case lists:keysearch(ModName, #mod_win.name, S#state.mod_wins) of false -> WxEnv = wx:get_env(), - {ok, Pid} = reltool_mod_win:start_link(WxEnv, Xref, RelPid, C, ModName), + {ok, Pid} = + reltool_mod_win:start_link(WxEnv, Xref, RelPid, C, ModName), MW = #mod_win{name = ModName, pid = Pid}, S#state{mod_wins = [MW | S#state.mod_wins]}; {value, MW} -> @@ -578,7 +647,9 @@ create_mod_window(#state{parent_pid = RelPid, xref_pid = Xref, common = C} = S, end. handle_mod_button(#state{app = App} = S, Items, Action) -> - App2 = lists:foldl(fun(Item, A) -> move_mod(A, Item, Action) end, App, Items), + App2 = lists:foldl(fun(Item, A) -> move_mod(A, Item, Action) end, + App, + Items), {ok, App3} = reltool_sys_win:set_app(S#state.parent_pid, App2), S2 = S#state{app = App3}, redraw_window(S2). @@ -587,7 +658,7 @@ move_mod(App, {_ItemNo, ModStr}, Action) -> ModName = list_to_atom(ModStr), Mods = App#app.mods, {value, M} = lists:keysearch(ModName, #mod.name, Mods), - AppCond = + AppCond = case Action of whitelist_add -> case M#mod.incl_cond of @@ -597,12 +668,13 @@ move_mod(App, {_ItemNo, ModStr}, Action) -> end; whitelist_del -> undefined; - blacklist_add -> + blacklist_add -> exclude; blacklist_del -> undefined; _ -> - error_logger:format("~p~p got unexpected mod button event: ~p\n\t ~p\n", + error_logger:format("~p~p got unexpected mod " + "button event: ~p\n\t ~p\n", [?MODULE, self(), ModName, Action]), M#mod.incl_cond end, @@ -623,7 +695,10 @@ change_mod_cond(S, App, NewModCond) -> redraw_window(S2). change_version(S, App, NewDir) -> - App2 = App#app{active_dir = NewDir, label = undefined, vsn = undefined, info = undefined}, + App2 = App#app{active_dir = NewDir, + label = undefined, + vsn = undefined, + info = undefined}, {ok, App3} = reltool_sys_win:set_app(S#state.parent_pid, App2), Title = app_title(App3), wxFrame:setTitle(S#state.frame, Title), @@ -635,8 +710,14 @@ redraw_apps(#state{app = #app{info = AppInfo}, app_incl_ctrl = InclCtrl, app_uses_ctrl = UsesCtrl, xref_pid = Xref}, - {_SourceMods, _WhiteMods, _BlackMods, _DerivedMods, UsedByMods, UsesMods}) -> - UsedByApps = lists:usort([{M#mod.app_name, Image} || {Image, _, M} <- UsedByMods]), + {_SourceMods, + _WhiteMods, + _BlackMods, + _DerivedMods, + UsedByMods, + UsesMods}) -> + UsedByApps = + lists:usort([{M#mod.app_name, Image} || {Image, _, M} <- UsedByMods]), Select = fun(AppName) -> {ok, App} = reltool_server:get_app(Xref, AppName), @@ -647,7 +728,8 @@ redraw_apps(#state{app = #app{info = AppInfo}, end, RequiredApps = lists:sort(lists:map(Select, AppInfo#app_info.applications)), InclApps = lists:map(Select, AppInfo#app_info.incl_apps), - UsesApps = lists:usort([{M#mod.app_name, Image} || {Image, _, M} <- UsesMods]), + UsesApps = + lists:usort([{M#mod.app_name, Image} || {Image, _, M} <- UsesMods]), do_redraw_apps(UsedByCtrl, UsedByApps), do_redraw_apps(RequiredCtrl, RequiredApps), do_redraw_apps(InclCtrl, InclApps), @@ -656,19 +738,26 @@ redraw_apps(#state{app = #app{info = AppInfo}, do_redraw_apps(ListCtrl, []) -> wxListCtrl:deleteAllItems(ListCtrl); - %% wxListCtrl:setColumnWidth(ListCtrl, ?APPS_APP_COL, ?wxLIST_AUTOSIZE_USEHEADER); + %% wxListCtrl:setColumnWidth(ListCtrl, ?APPS_APP_COL, +%% ?wxLIST_AUTOSIZE_USEHEADER); do_redraw_apps(ListCtrl, AppImages) -> wxListCtrl:deleteAllItems(ListCtrl), Add = fun({AppName, ImageId}, {Row, Prev}) when AppName =/= Prev -> - wxListCtrl:insertItem(ListCtrl, Row, ""), - if (Row rem 2) =:= 0 -> - wxListCtrl:setItemBackgroundColour(ListCtrl, Row, {240,240,255}); + wxListCtrl:insertItem(ListCtrl, Row, ""), + if (Row rem 2) =:= 0 -> + wxListCtrl:setItemBackgroundColour(ListCtrl, + Row, + {240,240,255}); true -> ignore end, Str = atom_to_list(AppName), - wxListCtrl:setItem(ListCtrl, Row, ?APPS_APP_COL, Str, [{imageId, ImageId}]), + wxListCtrl:setItem(ListCtrl, + Row, + ?APPS_APP_COL, + Str, + [{imageId, ImageId}]), {Row + 1, AppName}; ({_, _}, Acc) -> Acc @@ -688,8 +777,13 @@ redraw_mods(#state{mods_source_ctrl = SourceCtrl, deps_uses_ctrl = UsesCtrl, app = #app{is_pre_included = IsPre, is_included = IsIncl}, status_bar = Bar}, - {SourceMods, WhiteMods, BlackMods, DerivedMods, UsedByMods, UsesMods}) -> - InclStatus = + {SourceMods, + WhiteMods, + BlackMods, + DerivedMods, + UsedByMods, + UsesMods}) -> + InclStatus = case IsIncl of true when IsPre =:= true -> "Whitelist - "; true -> "Derived - "; @@ -711,7 +805,7 @@ app_to_mods(#state{xref_pid = Xref, app = App}) -> SourceMods = [M || M <- App#app.mods, M#mod.is_included =/= true, M#mod.is_pre_included =/= false], - WhiteMods = [M || M <- App#app.mods, + WhiteMods = [M || M <- App#app.mods, M#mod.is_pre_included =:= true], BlackMods = [M || M <- App#app.mods, M#mod.is_pre_included =:= false], @@ -722,7 +816,8 @@ app_to_mods(#state{xref_pid = Xref, app = App}) -> fun(ModName) when is_atom(ModName) -> {ok, M} = reltool_server:get_mod(Xref, ModName), if - M#mod.app_name =:= App#app.name, M#mod.is_included =:= true -> + M#mod.app_name =:= App#app.name, + M#mod.is_included =:= true -> false; true -> {true, M} @@ -780,20 +875,26 @@ opt_redraw_mods(undefined, _ImageMods) -> opt_redraw_mods(ListCtrl, ImageMods) -> HasApps = (wxListCtrl:getColumnCount(ListCtrl) > 1), do_redraw_mods(ListCtrl, ImageMods, HasApps). - + do_redraw_mods(ListCtrl, [], _HasApps) -> wxListCtrl:deleteAllItems(ListCtrl); do_redraw_mods(ListCtrl, ImageMods, HasApps) -> wxListCtrl:deleteAllItems(ListCtrl), Add = fun({ImageId, AppName, #mod{name = ModName}}, Row) -> - wxListCtrl:insertItem(ListCtrl, Row, ""), - if (Row rem 2) =:= 0 -> - wxListCtrl:setItemBackgroundColour(ListCtrl, Row, {240,240,255}); + wxListCtrl:insertItem(ListCtrl, Row, ""), + if (Row rem 2) =:= 0 -> + wxListCtrl:setItemBackgroundColour(ListCtrl, + Row, + {240,240,255}); true -> ignore end, - wxListCtrl:setItem(ListCtrl, Row, ?MODS_MOD_COL, atom_to_list(ModName), [{imageId, ImageId}]), + wxListCtrl:setItem(ListCtrl, + Row, + ?MODS_MOD_COL, + atom_to_list(ModName), + [{imageId, ImageId}]), case HasApps of false -> ok; @@ -842,13 +943,14 @@ redraw_config(#state{sys = #sys{incl_cond = GlobalIncl, SelectedRadio, SourceBox, fun(true) -> - reltool_utils:elem_to_index(ActiveDir, SortedDirs) - 1; + reltool_utils:elem_to_index(ActiveDir, + SortedDirs) - 1; (false) -> 0 end). redraw_double_box(Global, Local, GlobalRadio, LocalRadio, LocalBox, GetChoice) -> - AppCond = + AppCond = case Local of undefined -> wxRadioButton:setValue(GlobalRadio, true), diff --git a/lib/reltool/src/reltool_fgraph.erl b/lib/reltool/src/reltool_fgraph.erl index 09c4f8c8ce..2e8f39e418 100644 --- a/lib/reltool/src/reltool_fgraph.erl +++ b/lib/reltool/src/reltool_fgraph.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2009-2010. 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_fgraph). @@ -80,7 +80,7 @@ foreach(Fun, Fg) -> end, Fg), Fg. -map(Fun, Fg) -> +map(Fun, Fg) -> lists:foreach(fun (Key) -> put(Key,Fun(get(Key))) end, Fg), @@ -105,7 +105,9 @@ step(Vs, Es) -> step(Vs, Es, {0,0}). step(Vs, Es, Pa) -> ?MODULE:map(fun (Node = {_, #fg_v{ type = static }}) -> Node; - ({Key, Value = #fg_v{ p = {Px, Py}, v = {Vx, Vy}, type = dynamic}}) when is_float(Px), is_float(Py), is_float(Vx), is_float(Vy) -> + ({Key, Value = #fg_v{ p = {Px, Py}, v = {Vx, Vy}, type = dynamic}}) + when is_float(Px), is_float(Py), + is_float(Vx), is_float(Vy) -> F0 = {0.0,0.0}, F1 = coulomb_repulsion(Key, Value, Vs, F0), F2 = hooke_attraction(Key, Value, Vs, Es, F1), @@ -115,7 +117,7 @@ step(Vs, Es, Pa) -> Vx1 = (Vx + ?fg_th*Fx)*?fg_damp, Vy1 = (Vy + ?fg_th*Fy)*?fg_damp, - + Px1 = Px + ?fg_th*Vx1, Py1 = Py + ?fg_th*Vy1, @@ -123,14 +125,16 @@ step(Vs, Es, Pa) -> (Node) -> Node end, Vs). -point_attraction(_, #fg_v{ p = P0 }, Pa, {Fx, Fy}) when is_float(Fx), is_float(Fy) -> +point_attraction(_, #fg_v{ p = P0 }, Pa, {Fx, Fy}) + when is_float(Fx), is_float(Fy) -> K = 20, L = 150, {R, {Cx,Cy}} = composition(P0, Pa), F = -K*?fg_stretch*(R - L), {Fx + Cx*F, Fy + Cy*F}. - -coulomb_repulsion(K0, #fg_v{ p = P0, q = Q0}, Vs, {Fx0, Fy0}) when is_float(Fx0), is_float(Fy0) -> + +coulomb_repulsion(K0, #fg_v{ p = P0, q = Q0}, Vs, {Fx0, Fy0}) + when is_float(Fx0), is_float(Fy0) -> ?MODULE:foldl(fun ({K1, _}, F) when K1 == K0 -> F; ({_, #fg_v{ p = P1, q = Q1}}, {Fx, Fy}) -> @@ -140,7 +144,8 @@ coulomb_repulsion(K0, #fg_v{ p = P0, q = Q0}, Vs, {Fx0, Fy0}) when is_float(Fx0) (_, F) -> F end, {Fx0, Fy0}, Vs). -hooke_attraction(Key0, #fg_v{ p = P0 }, Vs, Es, {Fx0, Fy0}) when is_float(Fx0), is_float(Fy0) -> +hooke_attraction(Key0, #fg_v{ p = P0 }, Vs, Es, {Fx0, Fy0}) + when is_float(Fx0), is_float(Fy0) -> ?MODULE:foldl(fun ({{Key1,Key1}, _}, F) -> F; ({{Key1,Key2}, #fg_e{ l = L, k = K}}, {Fx, Fy}) when Key1 =:= Key0-> @@ -153,10 +158,11 @@ hooke_attraction(Key0, #fg_v{ p = P0 }, Vs, Es, {Fx0, Fy0}) when is_float(Fx0), {R, {Cx,Cy}} = composition(P0, P1), F = -K*?fg_stretch*(R - L), {Fx + Cx*F, Fy + Cy*F}; - (_, F) -> F + (_, F) -> F end, {Fx0, Fy0}, Es). -composition({Px1, Py1}, {Px0, Py0}) when is_float(Px1), is_float(Py1), is_float(Px0), is_float(Py0) -> +composition({Px1, Py1}, {Px0, Py0}) + when is_float(Px1), is_float(Py1), is_float(Px0), is_float(Py0) -> Dx = Px1 - Px0, Dy = Py1 - Py0, R = math:sqrt(Dx*Dx + Dy*Dy + 0.001), diff --git a/lib/reltool/src/reltool_fgraph_win.erl b/lib/reltool/src/reltool_fgraph_win.erl index b063fb94ba..b0deb1bab2 100644 --- a/lib/reltool/src/reltool_fgraph_win.erl +++ b/lib/reltool/src/reltool_fgraph_win.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2009-2010. 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_fgraph_win). @@ -95,7 +95,7 @@ change_node(Pid, Key, Color) -> Pid ! {change_node, Key, Color}. add_link(Pid, {FromKey, ToKey}) -> Pid ! {add_link, {FromKey, ToKey}}. del_link(Pid, {FromKey, ToKey}) -> Pid ! {del_link, {FromKey, ToKey}}. -stop(Pid, Reason) -> +stop(Pid, Reason) -> Ref = erlang:monitor(process, Pid), Pid ! {stop, Reason}, receive @@ -110,21 +110,24 @@ new(Parent, Options) -> Me = self(), Pid = spawn_link(fun() -> init([Parent, Me, Env, Options]) end), receive {Pid, {?MODULE, Panel}} -> {Pid,Panel} end. - + init([ParentWin, Pid, Env, Options]) -> wx:set_env(Env), - + BReset = wxButton:new(ParentWin, ?reset, [{label,"Reset"}]), BFreeze = wxButton:new(ParentWin, ?freeze, [{label,"Freeze"}]), BLock = wxButton:new(ParentWin, ?lock, [{label,"Lock"}]), BUnlock = wxButton:new(ParentWin, ?unlock, [{label,"Unlock"}]), BDelete = wxButton:new(ParentWin, ?delete, [{label,"Delete"}]), - SQ = wxSlider:new(ParentWin, ?q_slider, ?default_q, 1, 500, [{style, ?wxVERTICAL}]), - SL = wxSlider:new(ParentWin, ?l_slider, ?default_l, 1, 500, [{style, ?wxVERTICAL}]), - SK = wxSlider:new(ParentWin, ?k_slider, ?default_k, 1, 500, [{style, ?wxVERTICAL}]), + SQ = wxSlider:new(ParentWin, ?q_slider, ?default_q, 1, 500, + [{style, ?wxVERTICAL}]), + SL = wxSlider:new(ParentWin, ?l_slider, ?default_l, 1, 500, + [{style, ?wxVERTICAL}]), + SK = wxSlider:new(ParentWin, ?k_slider, ?default_k, 1, 500, + [{style, ?wxVERTICAL}]), Win = wxWindow:new(ParentWin, ?wxID_ANY, Options), - + ButtonSizer = wxBoxSizer:new(?wxVERTICAL), wxSizer:add(ButtonSizer, BReset), wxSizer:add(ButtonSizer, BFreeze), @@ -141,31 +144,34 @@ init([ParentWin, Pid, Env, Options]) -> WindowSizer = wxBoxSizer:new(?wxHORIZONTAL), wxSizer:add(WindowSizer, ButtonSizer, [{flag, ?wxEXPAND}, {proportion, 0}]), wxSizer:add(WindowSizer, Win, [{flag, ?wxEXPAND}, {proportion, 1}]), - + wxButton:setToolTip(BReset, "Remove selection and unlock all nodes."), wxButton:setToolTip(BFreeze, "Start/stop redraw of screen."), wxButton:setToolTip(BLock, "Lock all selected nodes."), wxButton:setToolTip(BUnlock, "Unlock all selected nodes."), wxButton:setToolTip(BDelete, "Delete all selected nodes."), - wxButton:setToolTip(SQ, "Control repulsive force. This can also be controlled with the mouse wheel on the canvas."), + wxButton:setToolTip(SQ, "Control repulsive force. This can also be" + " controlled with the mouse wheel on the canvas."), wxButton:setToolTip(SL, "Control link length."), wxButton:setToolTip(SK, "Control attractive force. Use with care."), - wxButton:setToolTip(Win, - "Drag mouse while left mouse button is pressed to perform various operations. " - "Combine with control key to select. Combine with shift key to lock single node."), + wxButton:setToolTip(Win, + "Drag mouse while left mouse button is pressed " + "to perform various operations. " + "Combine with control key to select. Combine " + "with shift key to lock single node."), wxButton:connect(BReset, command_button_clicked), wxButton:connect(BFreeze, command_button_clicked), wxButton:connect(BLock, command_button_clicked), wxButton:connect(BUnlock, command_button_clicked), wxButton:connect(BDelete, command_button_clicked), - + wxWindow:connect(SQ, command_slider_updated), wxWindow:connect(SL, command_slider_updated), wxWindow:connect(SK, command_slider_updated), - - wxWindow:connect(Win, enter_window), + + wxWindow:connect(Win, enter_window), wxWindow:connect(Win, move), wxWindow:connect(Win, motion), wxWindow:connect(Win, mousewheel), @@ -174,7 +180,7 @@ init([ParentWin, Pid, Env, Options]) -> wxWindow:connect(Win, left_up), wxWindow:connect(Win, right_down), wxWindow:connect(Win, paint, [{skip, true}]), - + Pen = wxPen:new({0,0,0}, [{width, 3}]), Font = wxFont:new(12, ?wxSWISS, ?wxNORMAL, ?wxNORMAL,[]), Brush = wxBrush:new({0,0,0}), @@ -182,13 +188,13 @@ init([ParentWin, Pid, Env, Options]) -> Pid ! {self(), {?MODULE, WindowSizer}}, wxWindow:setFocus(Win), %% Get keyboard focus - + Vs = reltool_fgraph:new(), Es = reltool_fgraph:new(), Me = self(), Ticker = spawn_link(fun() -> ticker_init(Me) end), - + loop( #state{ parent_pid = Pid, q_slider = SQ, l_slider = SL, @@ -215,14 +221,17 @@ graph_add_node(Key, Color, G = #graph{ vs = Vs}) -> M = 0.5, % mass P = {float(450 + random:uniform(100)), float(450 + random:uniform(100))}, - G#graph{ vs = reltool_fgraph:add(Key, #fg_v{ p = P, m = M, q = Q, color = Color}, Vs)}. + G#graph{ vs = reltool_fgraph:add(Key, + #fg_v{ p = P, m = M, q = Q, color = Color}, + Vs)}. graph_change_node(Key, Color, G) -> case reltool_fgraph:get(Key, G#graph.vs) of - undefined -> + undefined -> G; V -> - G#graph{ vs = reltool_fgraph:set(Key, V#fg_v{ color = Color }, G#graph.vs)} + G#graph{ vs = reltool_fgraph:set(Key, V#fg_v{ color = Color }, + G#graph.vs)} end. graph_del_node(Key, G = #graph{ vs = Vs0, es = Es0}) -> @@ -231,7 +240,7 @@ graph_del_node(Key, G = #graph{ vs = Vs0, es = Es0}) -> G#graph{ vs = Vs, es = Es }. graph_add_link(Key0, Key1, G = #graph{ es = Es}) -> - K = 60.0, % attractive force + K = 60.0, % attractive force L = 5.0, % spring length G#graph{ es = reltool_fgraph:add({Key0, Key1}, #fg_e{ k = K, l = L}, Es) }. @@ -249,15 +258,17 @@ ticker_loop(Pid, Time) -> D = timer:now_diff(T1, T0)/1000, case round(40 - D) of Ms when Ms < 0 -> - %io:format("ticker: wait is 0 ms [fg ~7s ms] [fps ~7s]~n", [s(D), s(1000/D)]), + %io:format("ticker: wait is 0 ms [fg ~7s ms] [fps ~7s]~n", + % [s(D), s(1000/D)]), ticker_loop(Pid, 0); Ms -> - %io:format("ticker: wait is ~3s ms [fg ~7s ms] [fps ~7s]~n", [s(Ms), s(D), s(1000/40)]), + %io:format("ticker: wait is ~3s ms [fg ~7s ms] [fps ~7s]~n", + % [s(Ms), s(D), s(1000/40)]), ticker_loop(Pid, Ms) end end. -delete_edges(Es, []) -> +delete_edges(Es, []) -> Es; delete_edges(Es, [Key|Keys]) -> Edges = reltool_fgraph:foldl(fun @@ -269,7 +280,7 @@ delete_edges(Es, [Key|Keys]) -> (K, Esi) -> reltool_fgraph:del(K, Esi) end, Es, Edges), delete_edges(Es1, Keys). - + set_charge(Q, Vs) -> % Repulsive force F = fun({Key, Value}) -> {Key, Value#fg_v{ q = Q}} end, @@ -295,36 +306,47 @@ loop(S, G) -> wxSlider:setValue(S#state.k_slider, K), Es = set_length(L, G#graph.es), Es2 = set_spring(K, Es), - - Vs2 = reltool_fgraph:map(fun({Key, V}) -> - {Key, V#fg_v{selected = false, type = dynamic, q = Q}} - end, - G#graph.vs), - - {Xs, Ys} = reltool_fgraph:foldl(fun({_Key, #fg_v{p = {X, Y}}}, {Xs, Ys}) -> - {[X| Xs], [Y | Ys]} - end, - {[], []}, - Vs2), + + Vs2 = + reltool_fgraph:map(fun({Key, V}) -> + {Key, V#fg_v{selected = false, + type = dynamic, + q = Q}} + end, + G#graph.vs), + + {Xs, Ys} = + reltool_fgraph:foldl(fun({_Key, + #fg_v{p = {X, Y}}}, {Xs, Ys}) -> + {[X| Xs], [Y | Ys]} + end, + {[], []}, + Vs2), %% io:format("Before: ~p\n", [G#graph.offset]), Offset = case length(Xs) of 0 -> {0, 0}; N -> - MeanX = (lists:sum(Xs) / N), + MeanX = (lists:sum(Xs) / N), MeanY = (lists:sum(Ys) / N), {SizeX, SizeY} = wxWindow:getSize(S#state.window), - %% io:format("Min: ~p\n", [{lists:min(Xs), lists:min(Ys)}]), - %% io:format("Mean: ~p\n", [{MeanX, MeanY}]), - %% io:format("Max: ~p\n", [{lists:max(Xs), lists:max(Ys)}]), + %% io:format("Min: ~p\n", + %% [{lists:min(Xs), lists:min(Ys)}]), + %% io:format("Mean: ~p\n", + %% [{MeanX, MeanY}]), + %% io:format("Max: ~p\n", + %% [{lists:max(Xs), lists:max(Ys)}]), %% io:format("Size: ~p\n", [{SizeX, SizeY}]), %% {XM - (XS / 2), YM - (YS / 2)} %% {0 - lists:min(Xs) + 20, 0 - lists:min(Ys) + 20} {0 - MeanX + (SizeX / 2), 0 - MeanY + (SizeY / 2)} end, %% io:format("After: ~p\n", [Offset]), - loop(S, G#graph{vs = Vs2, es = Es2, offset = Offset, offset_state = false}); + loop(S, G#graph{vs = Vs2, + es = Es2, + offset = Offset, + offset_state = false}); #wx{id = ?freeze, event = #wxCommand{type=command_button_clicked}} -> %% Start/stop redraw of screen IsFrozen = @@ -354,10 +376,15 @@ loop(S, G) -> loop(S, G#graph{ vs = Vs }); #wx{id = ?delete, event = #wxCommand{type=command_button_clicked}} -> %% Delete all selected nodes - {Vs1, Keys} = reltool_fgraph:foldl(fun - ({Key, #fg_v{ selected = true}}, {Vs, Ks}) -> - {reltool_fgraph:del(Key,Vs), [Key|Ks]}; - (_, {Vs, Ks}) -> {Vs, Ks} + {Vs1, Keys} = + reltool_fgraph:foldl(fun + ({Key, + #fg_v{ selected = true}}, + {Vs, Ks}) -> + {reltool_fgraph:del(Key,Vs), + [Key|Ks]}; + (_, {Vs, Ks}) -> + {Vs, Ks} end, {G#graph.vs,[]}, G#graph.vs), Es = delete_edges(G#graph.es, Keys), loop(S, G#graph{ vs = Vs1, es = Es}); @@ -368,20 +395,26 @@ loop(S, G) -> #wx{id = ?move, event = #wxCommand{type=command_button_clicked}} -> loop(S#state{ mouse_act = ?move }, G); - #wx{id = ?q_slider, event = #wxCommand{type=command_slider_updated, commandInt = Q}} -> + #wx{id = ?q_slider, event = #wxCommand{type=command_slider_updated, + commandInt = Q}} -> loop(S, G#graph{ vs = set_charge(Q, G#graph.vs)}); - #wx{id = ?l_slider, event = #wxCommand{type=command_slider_updated, commandInt = L}} -> + #wx{id = ?l_slider, event = #wxCommand{type=command_slider_updated, + commandInt = L}} -> loop(S, G#graph{ es = set_length(L, G#graph.es)}); - #wx{id = ?k_slider, event = #wxCommand{type=command_slider_updated, commandInt = K}} -> + #wx{id = ?k_slider, event = #wxCommand{type=command_slider_updated, + commandInt = K}} -> loop(S, G#graph{ es = set_spring(K, G#graph.es)}); #wx{event=#wxKey{type=key_up, keyCode = 127}} -> % delete {Vs1, Keys} = - reltool_fgraph:foldl(fun({Key, #fg_v{ selected = true}}, {Vs, Ks}) -> - {reltool_fgraph:del(Key,Vs), [Key|Ks]}; - (_, {Vs, Ks}) -> - {Vs, Ks} - end, - {G#graph.vs,[]}, G#graph.vs), + reltool_fgraph:foldl(fun({Key, + #fg_v{ selected = true}}, + {Vs, Ks}) -> + {reltool_fgraph:del(Key,Vs), + [Key|Ks]}; + (_, {Vs, Ks}) -> + {Vs, Ks} + end, + {G#graph.vs,[]}, G#graph.vs), Es = delete_edges(G#graph.es, Keys), loop(S, G#graph{ vs = Vs1, es = Es}); #wx{event=#wxKey{type=key_up}} -> @@ -390,7 +423,11 @@ loop(S, G) -> loop(S, G); %% mouse - #wx{event=#wxMouse{type=left_down, shiftDown=Shift, controlDown=Ctrl, x=X, y=Y}} -> + #wx{event=#wxMouse{type=left_down, + shiftDown=Shift, + controlDown=Ctrl, + x=X, + y=Y}} -> if Shift -> loop(S, mouse_left_down_move(G, {X,Y})); @@ -401,7 +438,11 @@ loop(S, G) -> S#state.mouse_act =:= ?select -> loop(S, mouse_left_down_select(G, {X,Y})) end; - #wx{event=#wxMouse{type=motion, shiftDown=Shift, controlDown=Ctrl, x=X, y=Y}} -> + #wx{event=#wxMouse{type=motion, + shiftDown=Shift, + controlDown=Ctrl, + x=X, + y=Y}} -> if Shift -> loop(S, mouse_motion_move(G, {X,Y})); @@ -412,7 +453,9 @@ loop(S, G) -> S#state.mouse_act =:= ?select -> loop(S, mouse_motion_select(G, {X,Y})) end; - #wx{event=#wxMouse{type=left_up, shiftDown=Shift, controlDown=Ctrl, x=X, y=Y}} -> + #wx{event=#wxMouse{type=left_up, + shiftDown=Shift, + controlDown=Ctrl, x=X, y=Y}} -> if Shift -> loop(S, mouse_left_up_move(G, {X,Y}, Shift)); @@ -424,7 +467,7 @@ loop(S, G) -> loop(S, mouse_left_up_select(G, {X,Y})) end; - #wx{event=#wxMouse{type=right_down,x=_X,y=_Y}} -> + #wx{event=#wxMouse{type=right_down,x=_X,y=_Y}} -> loop(S, G); %% mouse wheel #wx{event=#wxMouse{type=mousewheel, wheelRotation=Rotation}} -> @@ -436,7 +479,7 @@ loop(S, G) -> Rotation < 0 -> wxSlider:setValue(S#state.q_slider, Q + 4), loop(S, G#graph{ vs = set_charge(Q + 4, G#graph.vs) }); - true -> + true -> loop(S, G) end; @@ -448,7 +491,7 @@ loop(S, G) -> redraw(S, G), loop(S, G); #wx{obj=Win,event=#wxMouse{type=enter_window}} -> - wxWindow:setFocus(Win), + wxWindow:setFocus(Win), loop(S, G); %% Graph manipulation @@ -465,9 +508,11 @@ loop(S, G) -> {Req, redraw} -> {SizeX, SizeY} = wxWindow:getSize(S#state.window), - Vs = reltool_fgraph:step(G#graph.vs, G#graph.es, {SizeX/2.0 - 20.0, SizeY/2.0}), + Vs = reltool_fgraph:step(G#graph.vs, + G#graph.es, + {SizeX/2.0 - 20.0, SizeY/2.0}), case S#state.is_frozen of - false -> + false -> Req ! {self(), ok}; true -> ignore @@ -481,7 +526,7 @@ loop(S, G) -> Other -> error_logger:format("~p~p got unexpected message:\n\t~p\n", - [?MODULE, self(), Other]), + [?MODULE, self(), Other]), loop(S, G) end. @@ -494,17 +539,22 @@ mouse_left_down_move(#graph{vs = Vs} = G, {X, Y}) -> false -> G#graph{ offset_state = {X,Y}}; {true, Key} -> - V = #fg_v{ type = Type} = reltool_fgraph:get(Key, Vs), - G#graph{ vs = reltool_fgraph:set(Key, V#fg_v{ type = moving}, Vs), select = {node, Key, Type, X, Y} } + V = #fg_v{ type = Type} = reltool_fgraph:get(Key, Vs), + G#graph{ vs = reltool_fgraph:set(Key, + V#fg_v{ type = moving}, Vs), + select = {node, Key, Type, X, Y} } end. coord_to_key(#graph{vs = Vs, offset = {Xo, Yo}}, {X, Y}) -> Xr = X - Xo, Yr = Y - Yo, - reltool_fgraph:foldl(fun({Key, #fg_v{ p = {Px, Py}}}, _) when abs(Px - Xr) < 10, - abs(Py - Yr) < 10 -> {true, Key}; - (_, Out) -> Out - end, false, Vs). + reltool_fgraph:foldl(fun({Key, #fg_v{ p = {Px, Py}}}, _) + when abs(Px - Xr) < 10, + abs(Py - Yr) < 10 -> + {true, Key}; + (_, Out) -> + Out + end, false, Vs). mouse_left_up_select(G, {_X,_Y}) -> case G#graph.select of @@ -524,7 +574,7 @@ mouse_left_up_select(G, {_X,_Y}) -> _ -> G#graph{ select = none} end. - + mouse_left_up_move(G = #graph{ select = Select, vs = Vs} = G, {X,Y}, Shift) -> case Select of {node, Key, _, X, Y} -> @@ -543,7 +593,7 @@ mouse_left_up_move(G = #graph{ select = Select, vs = Vs} = G, {X,Y}, Shift) -> _ -> G#graph{ select = none, offset_state = false } end. - + mouse_motion_select(G, {X,Y}) -> case G#graph.select of {P0, _P1} -> G#graph{ select = {P0, {X,Y}}}; @@ -557,11 +607,11 @@ mouse_motion_move(G = #graph{ select = {node, Key, _, _, _}, vs = Vs}, {X,Y}) -> G#graph{ vs = reltool_fgraph:set(Key, V2, Vs) }; mouse_motion_move(G, {X,Y}) -> case G#graph.offset_state of - {X1,Y1} -> + {X1,Y1} -> {X0, Y0} = G#graph.offset, G#graph{ offset_state = {X,Y}, offset = {X0 - (X1 - X), Y0 - (Y1 - Y)} }; - _ -> + _ -> G end. @@ -574,9 +624,9 @@ redraw(#state{window=Win}, G) -> wxClientDC:destroy(DC0), ok. -redraw(DC, _Size, G) -> - wx:batch(fun() -> - +redraw(DC, _Size, G) -> + wx:batch(fun() -> + Pen = G#graph.pen, Font = G#graph.font, Brush = G#graph.brush, @@ -587,7 +637,7 @@ redraw(DC, _Size, G) -> wxPen:setWidth(Pen, 1), wxDC:clear(DC), - % draw vertices and edges + % draw vertices and edges wxPen:setColour(Pen, ?color_fg), wxDC:setPen(DC,Pen), @@ -602,7 +652,9 @@ redraw(DC, _Size, G) -> % draw information text wxFont:setWeight(Font,?wxNORMAL), - draw_text(DC, reltool_fgraph:size(G#graph.vs), reltool_fgraph:size(G#graph.es), G#graph.ke), + draw_text(DC, + reltool_fgraph:'size'(G#graph.vs), + reltool_fgraph:'size'(G#graph.es), G#graph.ke), ok end). @@ -612,14 +664,14 @@ draw_select_box(DC, {{X0,Y0}, {X1,Y1}}) -> draw_line(DC, {X1,Y1}, {X0,Y1}, {0,0}), draw_line(DC, {X0,Y0}, {X0,Y1}, {0,0}), ok; -draw_select_box(_DC, _) -> +draw_select_box(_DC, _) -> ok. draw_es(DC, Vs, Es, Po, Pen, Brush) -> reltool_fgraph:foreach(fun ({{K1, K2}, _}) -> - #fg_v{ p = P1} = reltool_fgraph:get(K1, Vs), - #fg_v{ p = P2} = reltool_fgraph:get(K2, Vs), + #fg_v{ p = P1} = reltool_fgraph:'get'(K1, Vs), + #fg_v{ p = P2} = reltool_fgraph:'get'(K2, Vs), draw_arrow(DC, P1, P2, Po, Pen, Brush) end, Es). @@ -650,10 +702,15 @@ draw_arrow(DC, {X0,Y0}, {X1, Y1}, {X, Y}, Pen, Brush) -> wxDC:drawPolygon(DC, Points, []). draw_line(DC, {X0,Y0}, {X1, Y1}, {X, Y}) -> - wxDC:drawLine(DC, {round(X0 + X), round(Y0 + Y)}, {round(X1 + X), round(Y1 + Y)}). - + wxDC:drawLine(DC, + {round(X0 + X), round(Y0 + Y)}, + {round(X1 + X), round(Y1 + Y)}). + draw_vs(DC, Vs, {Xo, Yo}, Pen, Brush) -> - reltool_fgraph:foreach(fun({Key, #fg_v{ p ={X, Y}, color = Color, selected = Sel}}) -> + reltool_fgraph:foreach(fun({Key, + #fg_v{p ={X, Y}, + color = Color, + selected = Sel}}) -> String = s(Key), case Sel of true -> @@ -661,35 +718,49 @@ draw_vs(DC, Vs, {Xo, Yo}, Pen, Brush) -> wxBrush:setColour(Brush, ?color_bg), wxDC:setPen(DC,Pen), wxDC:setBrush(DC, Brush), - SelProps = {round(X-12 + Xo), round(Y-12 + Yo), 24, 24}, - wxDC:drawRoundedRectangle(DC, SelProps, float(?ARC_R)), + SelProps = {round(X-12 + Xo), + round(Y-12 + Yo), + 24, + 24}, + wxDC:drawRoundedRectangle(DC, + SelProps, + float(?ARC_R)), ok; false -> ok end, case Color of - default -> + default -> wxPen:setColour(Pen, ?color_default), - wxBrush:setColour(Brush, ?color_default_bg); - alternate -> - wxPen:setColour(Pen, ?color_alternate), - wxBrush:setColour(Brush, ?color_alternate_bg); + wxBrush:setColour(Brush, + ?color_default_bg); + alternate -> + wxPen:setColour(Pen, + ?color_alternate), + wxBrush:setColour(Brush, + ?color_alternate_bg); {FgColor, BgColor} -> wxPen:setColour(Pen, FgColor), - wxBrush:setColour(Brush, BgColor); + wxBrush:setColour(Brush, BgColor); Color -> wxPen:setColour(Pen, Color), wxBrush:setColour(Brush, Color) end, wxDC:setPen(DC,Pen), wxDC:setBrush(DC, Brush), - NodeProps = {round(X-8 + Xo),round(Y-8 + Yo),17,17}, - wxDC:drawRoundedRectangle(DC, NodeProps, float(?ARC_R)), - wxDC:drawText(DC, String, {round(X + Xo), round(Y + Yo)}), + NodeProps = {round(X-8 + Xo), + round(Y-8 + Yo),17,17}, + wxDC:drawRoundedRectangle(DC, + NodeProps, + float(?ARC_R)), + wxDC:drawText(DC, + String, + {round(X + Xo), + round(Y + Yo)}), ok; (_) -> ok - end, + end, Vs). draw_text(DC, Nvs, Nes, _KE) -> @@ -720,7 +791,7 @@ calc_point({X, Y}, Length, Radians) -> %% %% Convert from an angle in radians to degrees %% radians_to_degrees(Radians) -> %% Radians * 180 / math:pi(). -%% +%% %% %% Convert from an angle in degrees to radians %% degrees_to_radians(Degrees) -> %% Degrees * math:pi() / 180. diff --git a/lib/reltool/src/reltool_mod_win.erl b/lib/reltool/src/reltool_mod_win.erl index c05f73cde8..e1c2fa5100 100644 --- a/lib/reltool/src/reltool_mod_win.erl +++ b/lib/reltool/src/reltool_mod_win.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2009-2011. 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_mod_win). @@ -34,7 +34,7 @@ -include_lib("wx/include/wx.hrl"). -include("reltool.hrl"). --record(state, +-record(state, {parent_pid, xref_pid, rel_pid, @@ -73,7 +73,7 @@ -define(WIN_HEIGHT, 600). -define(CLOSE_ITEM, ?wxID_EXIT). %% Use OS specific version if available --define(ABOUT_ITEM, ?wxID_ABOUT). %% Use OS specific +-define(ABOUT_ITEM, ?wxID_ABOUT). %% Use OS specific -define(CONTENTS_ITEM, 300). -define(SEARCH_ENTRY, 413). -define(GOTO_ENTRY, 414). @@ -87,7 +87,11 @@ %% Client start_link(WxEnv, Xref, RelPid, Common, ModName) -> - proc_lib:start_link(?MODULE, init, [self(), WxEnv, Xref, RelPid, Common, ModName], infinity, []). + proc_lib:start_link(?MODULE, + init, + [self(), WxEnv, Xref, RelPid, Common, ModName], + infinity, + []). raise(Pid) -> reltool_utils:cast(Pid, raise). @@ -127,10 +131,15 @@ loop(#state{xref_pid = Xref, common = C, mod = Mod} = S) -> receive Msg -> %% io:format("~s~p -> ~p\n", [S#state.name, self(), Msg]), - case Msg of + case Msg of {system, From, SysMsg} -> Dbg = C#common.sys_debug, - sys:handle_system_msg(SysMsg, From, S#state.parent_pid, ?MODULE, Dbg, S); + sys:handle_system_msg(SysMsg, + From, + S#state.parent_pid, + ?MODULE, + Dbg, + S); {cast, _From, raise} -> wxFrame:raise(S#state.frame), wxFrame:setFocus(S#state.frame), @@ -169,7 +178,7 @@ loop(#state{xref_pid = Xref, common = C, mod = Mod} = S) -> create_window(#state{mod = Mod, name = ModStr} = S) -> Title = atom_to_list(?APPLICATION) ++ " - " ++ - atom_to_list(Mod#mod.app_name) ++ " - " ++ + atom_to_list(Mod#mod.app_name) ++ " - " ++ ModStr ++ ".erl", Frame = wxFrame:new(wx:null(), ?wxID_ANY, Title, []), %% wxFrame:setSize(Frame, {?WIN_WIDTH, ?WIN_HEIGHT}), @@ -177,7 +186,7 @@ create_window(#state{mod = Mod, name = ModStr} = S) -> StatusBar = wxFrame:createStatusBar(Frame,[]), Book = wxNotebook:new(Panel, ?wxID_ANY, []), - + S2 = S#state{frame = Frame, panel = Panel, book = Book, @@ -204,11 +213,17 @@ create_deps_page(S) -> Panel = wxPanel:new(S#state.book, []), Main = wxBoxSizer:new(?wxHORIZONTAL), - UsedByCtrl = create_mods_list_ctrl(Panel, Main, "Modules used by others", " and their applications"), + UsedByCtrl = create_mods_list_ctrl(Panel, + Main, + "Modules used by others", + " and their applications"), wxSizer:add(Main, wxStaticLine:new(Panel, [{style, ?wxLI_VERTICAL}]), [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}]), - UsesCtrl = create_mods_list_ctrl(Panel, Main, "Used modules", " and their applications"), + UsesCtrl = create_mods_list_ctrl(Panel, + Main, + "Used modules", + " and their applications"), S2 = S#state{deps_used_by_ctrl = UsedByCtrl, deps_uses_ctrl = UsesCtrl}, redraw_mods(S2), @@ -242,8 +257,10 @@ create_mods_list_ctrl(Panel, Sizer, ModText, AppText) -> %% wxListCtrl:setColumnWidth(ListCtrl, ?MODS_APP_COL, ?MODS_APP_COL_WIDTH), wxListItem:destroy(ListItem), - wxEvtHandler:connect(ListCtrl, size, [{skip, true}, {userData, mods_list_ctrl}]), - wxListCtrl:connect(ListCtrl, command_list_item_activated, [{userData, open_app}]), + wxEvtHandler:connect(ListCtrl, size, + [{skip, true}, {userData, mods_list_ctrl}]), + wxListCtrl:connect(ListCtrl, command_list_item_activated, + [{userData, open_app}]), wxWindow:connect(ListCtrl, enter_window), wxSizer:add(Sizer, ListCtrl, @@ -252,7 +269,8 @@ create_mods_list_ctrl(Panel, Sizer, ModText, AppText) -> {proportion, 1}]), ListCtrl. -create_code_page(#state{book = Book, code_pages = Pages, name = ModStr} = S, PageName) -> +create_code_page(#state{book = Book, code_pages = Pages, name = ModStr} = S, + PageName) -> case find_page(S, PageName) of not_found -> Page = do_create_code_page(S, PageName), @@ -260,7 +278,7 @@ create_code_page(#state{book = Book, code_pages = Pages, name = ModStr} = S, Pag Pos = length(Pages2), wxNotebook:setSelection(Book, Pos), case find_page(S, ?INITIAL_CODE_PAGE_NAME) of - not_found -> + not_found -> ignore; {found, _, CodePos} -> %% Rename initial code page @@ -288,33 +306,37 @@ find_page([], _PageName, _Pos) -> do_create_code_page(#state{xref_pid = Xref, mod = M} = S, PageName) -> Panel = wxPanel:new(S#state.book, []), Editor = create_editor(Panel), - ToolTip = "Double click on a function call to search the function definition.", + ToolTip = "Double click on a function call to " + "search the function definition.", wxBitmapButton:setToolTip(Editor, ToolTip), {Objs, Data, SearchSz} = create_search_area(Panel), {ok, App} = reltool_server:get_app(Xref, M#mod.app_name), - ErlBin = + ErlBin = case App#app.is_escript of true -> find_escript_bin(App, M); false -> find_regular_bin(App, M) end, - + load_code(Editor, ErlBin), - + Sizer = wxBoxSizer:new(?wxVERTICAL), wxSizer:add(Sizer, Editor, [{flag, ?wxEXPAND}, {proportion, 1}]), wxSizer:add(Sizer, SearchSz, [{flag, ?wxEXPAND}]), wxPanel:setSizer(Panel, Sizer), wxNotebook:addPage(S#state.book, Panel, PageName, []), - #code_page{name = PageName, editor = Editor, find_objs = Objs, find_data = Data}. + #code_page{name = PageName, + editor = Editor, + find_objs = Objs, + find_data = Data}. find_regular_bin(App, Mod) -> ActiveDir = App#app.active_dir, SrcDir = filename:join([ActiveDir, "src"]), ModStr = atom_to_list(Mod#mod.name), - Base = ModStr ++ ".erl", - Find = fun(F, _Acc) -> file:read_file(F) end, - case filelib:fold_files(SrcDir, Base, true, Find, {error, enoent}) of + Base = "^" ++ ModStr ++ "\\.erl$", + Find = fun(F, _Acc) -> throw(file:read_file(F)) end, + case catch filelib:fold_files(SrcDir, Base, true, Find, {error, enoent}) of {ok, Bin} -> Bin; {error, enoent} -> @@ -322,9 +344,11 @@ find_regular_bin(App, Mod) -> BeamFile = filename:join([ActiveDir, "ebin", ModStr ++ ".beam"]), case beam_lib:chunks(BeamFile, [abstract_code]) of {ok,{_,[{abstract_code,{_,AC}}]}} -> - list_to_binary(erl_prettypr:format(erl_syntax:form_list(AC))); + IoList = erl_prettypr:format(erl_syntax:form_list(AC)), + list_to_binary(IoList); _ -> - list_to_binary(["%% Bad luck, cannot find any debug info in the file \"", BeamFile]) + list_to_binary(["%% Bad luck, cannot find any " + "debug info in the file \"", BeamFile]) end end. @@ -340,10 +364,17 @@ find_escript_bin(#app{active_dir = ActiveDir}, Mod) -> [_] -> Bin = GetBin(), case beam_lib:version(Bin) of - {ok,{M, _}} when M =:= ModName; FullName =:= "." -> - case beam_lib:chunks(Bin, [abstract_code]) of + {ok,{M, _}} when M =:= ModName; + FullName =:= "." -> + case beam_lib:chunks(Bin, + [abstract_code]) of {ok,{_,[{abstract_code,{_,AC}}]}} -> - {obj, list_to_binary(erl_prettypr:format(erl_syntax:form_list(AC)))}; + Form = + erl_syntax:form_list(AC), + IoList = + erl_prettypr:format(Form), + {obj, + list_to_binary(IoList)}; _ -> Acc end; @@ -363,10 +394,14 @@ find_escript_bin(#app{active_dir = ActiveDir}, Mod) -> {fun(FullName, _GetInfo, GetBin, Acc) -> io:format("", []), case filename:split(FullName) of - [_AppName, "ebin", F] when F =:= ObjFile, Acc =:= NotFound -> - case beam_lib:chunks(GetBin(), [abstract_code]) of + [_AppName, "ebin", F] + when F =:= ObjFile, Acc =:= NotFound -> + case beam_lib:chunks(GetBin(), + [abstract_code]) of {ok,{_,[{abstract_code,{_,AC}}]}} -> - {obj, list_to_binary(erl_prettypr:format(erl_syntax:form_list(AC)))}; + Form = erl_syntax:form_list(AC), + IoList = erl_prettypr:format(Form), + {obj, list_to_binary(IoList)}; _ -> Acc end; @@ -379,17 +414,19 @@ find_escript_bin(#app{active_dir = ActiveDir}, Mod) -> filename:dirname(ActiveDir)} end, try - case escript:foldl(Fun, NotFound, Escript) of + case reltool_utils:escript_foldl(Fun, NotFound, Escript) of {ok, {text, Bin}} -> Bin; {ok, {obj, Bin}} -> Bin; _ -> - list_to_binary(["%% Bad luck, cannot find the code in the escript ", Escript, "."]) + list_to_binary(["%% Bad luck, cannot find the " + "code in the escript ", Escript, "."]) end - catch + catch throw:Reason when is_list(Reason) -> - list_to_binary(["%% Bad luck, cannot find the code in the escript ", Escript, ": ", Reason]) + list_to_binary(["%% Bad luck, cannot find the code " + "in the escript ", Escript, ": ", Reason]) end. create_config_page(S) -> @@ -400,13 +437,16 @@ create_config_page(S) -> handle_event(#state{xref_pid = Xref} = S, Wx) -> %% io:format("wx: ~p\n", [Wx]), case Wx of - #wx{obj= ListCtrl, userData = mods_list_ctrl, event = #wxSize{type = size, size = {W, _H}}} -> + #wx{obj= ListCtrl, + userData = mods_list_ctrl, + event = #wxSize{type = size, size = {W, _H}}} -> wxListCtrl:setColumnWidth(ListCtrl, ?MODS_MOD_COL, (2 * W) div 3), wxListCtrl:setColumnWidth(ListCtrl, ?MODS_APP_COL, W div 3), S; #wx{userData = open_app, obj = ListCtrl, - event = #wxList{type = command_list_item_activated, itemIndex = Pos}} -> + event = #wxList{type = command_list_item_activated, + itemIndex = Pos}} -> ModStr = wxListCtrl:getItemText(ListCtrl, Pos), ModName = list_to_atom(ModStr), {ok, Mod} = reltool_server:get_mod(Xref, ModName), @@ -431,13 +471,15 @@ handle_event(#state{xref_pid = Xref} = S, Wx) -> Page = lists:nth(N, S#state.code_pages), S#state{active_page = Page} end; - #wx{event = #wxCommand{type = command_button_clicked}, userData = history_back} -> + #wx{event = #wxCommand{type = command_button_clicked}, + userData = history_back} -> goto_back(S); #wx{obj = ObjRef, event = #wxMouse{type = enter_window}} -> wxWindow:setFocus(ObjRef), S; _ -> - error_logger:format("~p~p got unexpected mod event from wx:\n\t~p\n", + error_logger:format("~p~p got unexpected mod event from " + "wx:\n\t~p\n", [?MODULE, self(), Wx]), S end. @@ -450,7 +492,7 @@ redraw_mods(#state{xref_pid = Xref, uses_mods = UsesModNames, used_by_mods = UsedByModNames}, status_bar = Bar}) -> - InclStatus = + InclStatus = case IsIncl of true when IsPre =:= true -> "Whitelist - "; true -> "Derived - "; @@ -458,8 +500,10 @@ redraw_mods(#state{xref_pid = Xref, undefined -> "Source - " end, Status = lists:concat([InclStatus, - " uses ", length(UsesModNames), " modules and ", - " is used by ", length(UsedByModNames), " modules."]), + " uses ", length(UsesModNames), + " modules and ", + " is used by ", length(UsedByModNames), + " modules."]), wxStatusBar:setStatusText(Bar, Status), UsesMods = [select_image(Xref, M) || M <- UsesModNames], UsedByMods = [select_image(Xref, M) || M <- UsedByModNames], @@ -470,7 +514,7 @@ select_image(Xref, ModName) -> {ok, M} = reltool_server:get_mod(Xref, ModName), Image = case M#mod.is_included of - _ when M#mod.app_name =:= ?MISSING_APP -> ?ERR_IMAGE; + _ when M#mod.app_name =:= ?MISSING_APP_NAME -> ?ERR_IMAGE; true -> ?TICK_IMAGE; false -> ?WARN_IMAGE; undefined -> ?ERR_IMAGE @@ -483,9 +527,11 @@ redraw_mods(ListCtrl, ImageMods) -> wxListCtrl:deleteAllItems(ListCtrl), Add = fun({ImageId, AppName, #mod{name = ModName}}, Row) -> - wxListCtrl:insertItem(ListCtrl, Row, ""), - if (Row rem 2) =:= 0 -> - wxListCtrl:setItemBackgroundColour(ListCtrl, Row, {240,240,255}); + wxListCtrl:insertItem(ListCtrl, Row, ""), + if (Row rem 2) =:= 0 -> + wxListCtrl:setItemBackgroundColour(ListCtrl, + Row, + {240,240,255}); true -> ignore end, @@ -515,16 +561,16 @@ goto_line(#state{active_page = P} = S, LineNo) when is_integer(LineNo) -> wxStyledTextCtrl:setSelection(Editor, Left, Right), S; goto_line(#state{active_page = P} =S, Str) when is_list(Str) -> - try + try LineNo = list_to_integer(Str), CurrentPos = wxStyledTextCtrl:getCurrentPos(P#code_page.editor), S2 = add_pos_to_history(S, CurrentPos), goto_line(S2, LineNo - 1) - catch + catch _:_ -> wxStatusBar:setStatusText(S#state.status_bar, "Not a line number"), S - end. + end. find_string(S, Str) -> find_string(S, Str, 0). @@ -535,19 +581,20 @@ find_regexp_forward(S, Str) -> wxTextCtrl:setValue(TextCtrl, Str), S2. -find_string(#state{active_page = #code_page{editor = Editor, - find_objs = #find_objs{radio={NextO,_,CaseO}}, - find_data = #find_data{found = Found} = Data} = P} = S, +find_string(#state{active_page = + #code_page{editor = Editor, + find_objs = #find_objs{radio={NextO,_,CaseO}}, + find_data = #find_data{found = Found} = Data} = P} = S, Str, Flag) -> wxStyledTextCtrl:hideSelection(Editor, true), Dir = wxRadioButton:getValue(NextO) xor wx_misc:getKeyState(?WXK_SHIFT), Case = wxCheckBox:getValue(CaseO), Pos = - if + if Found, Dir -> %% Forward Continuation wxStyledTextCtrl:getAnchor(Editor); - Found -> %% Backward Continuation + Found -> %% Backward Continuation wxStyledTextCtrl:getCurrentPos(Editor); Dir -> %% Forward wrap 0; @@ -556,18 +603,18 @@ find_string(#state{active_page = #code_page{editor = Editor, end, wxStyledTextCtrl:gotoPos(Editor,Pos), wxStyledTextCtrl:searchAnchor(Editor), - Flag2 = + Flag2 = if Case -> Flag bor ?wxSTC_FIND_MATCHCASE; true -> Flag end, - Res = - if + Res = + if Dir -> wxStyledTextCtrl:searchNext(Editor, Flag2, Str); true -> wxStyledTextCtrl:searchPrev(Editor, Flag2, Str) end, - Found2 = + Found2 = case Res >= 0 of - true -> + true -> wxStyledTextCtrl:hideSelection(Editor, false), %% io:format("Found ~p ~n",[Res]), LineNo = wxStyledTextCtrl:lineFromPosition(Editor,Res), @@ -576,11 +623,15 @@ find_string(#state{active_page = #code_page{editor = Editor, true; false -> wxStatusBar:setStatusText(S#state.status_bar, - "Not found (Hit Enter to wrap search)"), + "Not found (Hit Enter to " + "wrap search)"), false - end, + end, P2 = P#code_page{find_data = Data#find_data{found = Found2}}, - Pages = lists:keystore(P#code_page.name, #code_page.name, S#state.code_pages, P2), + Pages = lists:keystore(P#code_page.name, + #code_page.name, + S#state.code_pages, + P2), S#state{active_page = P2, code_pages = Pages}. goto_function(S, Editor) -> @@ -589,14 +640,14 @@ goto_function(S, Editor) -> Left = wxStyledTextCtrl:wordStartPosition(Editor, CurrentPos, true), Right = wxStyledTextCtrl:wordEndPosition(Editor, CurrentPos, true), ColonPos = Left - 1, - Left2 = + Left2 = case wxStyledTextCtrl:getCharAt(Editor, ColonPos) of $: -> wxStyledTextCtrl:wordStartPosition(Editor, ColonPos, true); _ -> Left end, - Right2 = + Right2 = case wxStyledTextCtrl:getCharAt(Editor, Right) of $: -> wxStyledTextCtrl:wordEndPosition(Editor, Right + 1, true); @@ -623,33 +674,41 @@ do_goto_function(#state{active_page = P} = S, [FunName]) -> find_regexp_forward(S, "^" ++ FunName ++ "("); do_goto_function(S, [ModStr, FunStr]) -> case reltool_server:get_mod(S#state.xref_pid, list_to_atom(ModStr)) of - {ok, Mod} when Mod#mod.app_name =/= ?MISSING_APP -> + {ok, Mod} when Mod#mod.app_name =/= ?MISSING_APP_NAME -> S2 = create_code_page(S#state{mod = Mod}, ModStr), find_regexp_forward(S2, "^" ++ FunStr ++ "("); {ok, _} -> - wxStatusBar:setStatusText(S#state.status_bar, "No such module: " ++ ModStr), + wxStatusBar:setStatusText(S#state.status_bar, + "No such module: " ++ ModStr), S end. -goto_back(#state{active_page = #code_page{editor = Editor, find_data = Data} = Page, +goto_back(#state{active_page = + #code_page{editor = Editor, find_data = Data} = Page, code_pages = Pages} = S) -> case Data#find_data.history of [PrevPos | History] -> LineNo = wxStyledTextCtrl:lineFromPosition(Editor, PrevPos), Data2 = Data#find_data{history = History}, Page2 = Page#code_page{find_data = Data2}, - Pages2 = lists:keystore(Page2#code_page.name, #code_page.name, Pages, Page2), - goto_line(S#state{active_page = Page2, code_pages = Pages2}, LineNo); + Pages2 = lists:keystore(Page2#code_page.name, + #code_page.name, + Pages, + Page2), + goto_line(S#state{active_page = Page2, code_pages = Pages2}, + LineNo); [] -> wxStatusBar:setStatusText(S#state.status_bar, "No history"), S end. -add_pos_to_history(#state{active_page = Page, code_pages = Pages} = S, CurrentPos) -> +add_pos_to_history(#state{active_page = Page, code_pages = Pages} = S, + CurrentPos) -> Data = Page#code_page.find_data, Data2 = Data#find_data{history = [CurrentPos | Data#find_data.history]}, Page2 = Page#code_page{find_data = Data2}, - Pages2 = lists:keystore(Page2#code_page.name, #code_page.name, Pages, Page2), + Pages2 = + lists:keystore(Page2#code_page.name, #code_page.name, Pages, Page2), S#state{active_page = Page2, code_pages = Pages2}. create_editor(Parent) -> @@ -685,24 +744,31 @@ create_editor(Parent) -> wxStyledTextCtrl:styleSetFont(Ed, Style, FixedFont), wxStyledTextCtrl:styleSetForeground(Ed, Style, Color) end, - [SetStyle(Style) || Style <- Styles], + lists:foreach(fun (Style) -> SetStyle(Style) end, Styles), wxStyledTextCtrl:setKeyWords(Ed, 0, keyWords()), %% Margins Markers %% Breakpoint Should be a pixmap? - wxStyledTextCtrl:markerDefine(Ed, 0, ?wxSTC_MARK_CIRCLE, [{foreground, {170,20,20}}]), - wxStyledTextCtrl:markerDefine(Ed, 0, ?wxSTC_MARK_CIRCLE, [{background, {200,120,120}}]), - %% Disabled Breakpoint - wxStyledTextCtrl:markerDefine(Ed, 1, ?wxSTC_MARK_CIRCLE, [{foreground, {20,20,170}}]), - wxStyledTextCtrl:markerDefine(Ed, 1, ?wxSTC_MARK_CIRCLE, [{background, {120,120,200}}]), - + wxStyledTextCtrl:markerDefine(Ed, 0, ?wxSTC_MARK_CIRCLE, + [{foreground, {170,20,20}}]), + wxStyledTextCtrl:markerDefine(Ed, 0, ?wxSTC_MARK_CIRCLE, + [{background, {200,120,120}}]), + %% Disabled Breakpoint + wxStyledTextCtrl:markerDefine(Ed, 1, ?wxSTC_MARK_CIRCLE, + [{foreground, {20,20,170}}]), + wxStyledTextCtrl:markerDefine(Ed, 1, ?wxSTC_MARK_CIRCLE, + [{background, {120,120,200}}]), + %% Current Line - wxStyledTextCtrl:markerDefine(Ed, 2, ?wxSTC_MARK_ARROW, [{foreground, {20,170,20}}]), - wxStyledTextCtrl:markerDefine(Ed, 2, ?wxSTC_MARK_ARROW, [{background, {200,255,200}}]), - wxStyledTextCtrl:markerDefine(Ed, 3, ?wxSTC_MARK_BACKGROUND, [{background, {200,255,200}}]), + wxStyledTextCtrl:markerDefine(Ed, 2, ?wxSTC_MARK_ARROW, + [{foreground, {20,170,20}}]), + wxStyledTextCtrl:markerDefine(Ed, 2, ?wxSTC_MARK_ARROW, + [{background, {200,255,200}}]), + wxStyledTextCtrl:markerDefine(Ed, 3, ?wxSTC_MARK_BACKGROUND, + [{background, {200,255,200}}]), %% Scrolling - Policy = ?wxSTC_CARET_SLOP bor ?wxSTC_CARET_JUMPS bor ?wxSTC_CARET_EVEN, + Policy = ?wxSTC_CARET_SLOP bor ?wxSTC_CARET_JUMPS bor ?wxSTC_CARET_EVEN, wxStyledTextCtrl:setYCaretPolicy(Ed, Policy, 3), wxStyledTextCtrl:setVisiblePolicy(Ed, Policy, 3), @@ -714,9 +780,9 @@ create_editor(Parent) -> create_search_area(Parent) -> Sizer = wxBoxSizer:new(?wxHORIZONTAL), - wxSizer:add(Sizer, wxStaticText:new(Parent, ?wxID_ANY, "Find:"), + wxSizer:add(Sizer, wxStaticText:new(Parent, ?wxID_ANY, "Find:"), [{flag,?wxALIGN_CENTER_VERTICAL}]), - TC1 = wxTextCtrl:new(Parent, ?SEARCH_ENTRY, [{style, ?wxTE_PROCESS_ENTER}]), + TC1 = wxTextCtrl:new(Parent, ?SEARCH_ENTRY, [{style, ?wxTE_PROCESS_ENTER}]), wxSizer:add(Sizer, TC1, [{proportion,3}, {flag, ?wxEXPAND}]), Nbtn = wxRadioButton:new(Parent, ?wxID_ANY, "Next"), wxRadioButton:setValue(Nbtn, true), @@ -726,14 +792,15 @@ create_search_area(Parent) -> Cbtn = wxCheckBox:new(Parent, ?wxID_ANY, "Match Case"), wxSizer:add(Sizer,Cbtn,[{flag,?wxALIGN_CENTER_VERTICAL}]), wxSizer:add(Sizer, 15,15, [{proportion,1}, {flag, ?wxEXPAND}]), - wxSizer:add(Sizer, wxStaticText:new(Parent, ?wxID_ANY, "Goto Line:"), + wxSizer:add(Sizer, wxStaticText:new(Parent, ?wxID_ANY, "Goto Line:"), [{flag,?wxALIGN_CENTER_VERTICAL}]), - TC2 = wxTextCtrl:new(Parent, ?GOTO_ENTRY, [{style, ?wxTE_PROCESS_ENTER}]), + TC2 = wxTextCtrl:new(Parent, ?GOTO_ENTRY, [{style, ?wxTE_PROCESS_ENTER}]), wxSizer:add(Sizer, TC2, [{proportion,0}, {flag, ?wxEXPAND}]), Button = wxButton:new(Parent, ?wxID_ANY, [{label, "Back"}]), wxSizer:add(Sizer, Button, []), - wxEvtHandler:connect(Button, command_button_clicked, [{userData, history_back}]), + wxEvtHandler:connect(Button, command_button_clicked, + [{userData, history_back}]), %% wxTextCtrl:connect(TC1, command_text_updated), wxTextCtrl:connect(TC1, command_text_enter), %% wxTextCtrl:connect(TC1, kill_focus), @@ -748,7 +815,9 @@ load_code(Ed, Code) when is_binary(Code) -> wxStyledTextCtrl:setTextRaw(Ed, <<Code/binary, 0:8>>), Lines = wxStyledTextCtrl:getLineCount(Ed), Sz = trunc(math:log10(Lines))+1, - LW = wxStyledTextCtrl:textWidth(Ed, ?wxSTC_STYLE_LINENUMBER, lists:duplicate(Sz, $9)), + LW = wxStyledTextCtrl:textWidth(Ed, + ?wxSTC_STYLE_LINENUMBER, + lists:duplicate(Sz, $9)), %%io:format("~p ~p ~p~n", [Lines, Sz, LW]), wxStyledTextCtrl:setMarginWidth(Ed, 0, LW+5), wxStyledTextCtrl:setReadOnly(Ed, true), diff --git a/lib/reltool/src/reltool_server.erl b/lib/reltool/src/reltool_server.erl index 8d4530131f..d7cad8b29e 100644 --- a/lib/reltool/src/reltool_server.erl +++ b/lib/reltool/src/reltool_server.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2009-2011. 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_server). @@ -44,7 +44,7 @@ -include("reltool.hrl"). --record(state, +-record(state, {options, parent_pid, common, @@ -60,16 +60,20 @@ start_link() -> start_link([]). start_link(Options) -> - proc_lib:start_link(?MODULE, init, [[{parent, self()} | Options]], infinity, []). + proc_lib:start_link(?MODULE, + init, + [[{parent, self()} | Options]], + infinity, + []). -get_config(Pid, InclDefaults, InclDerivates) -> - reltool_utils:call(Pid, {get_config, InclDefaults, InclDerivates}). +get_config(Pid, InclDef, InclDeriv) -> + reltool_utils:call(Pid, {get_config, InclDef, InclDeriv}). load_config(Pid, FilenameOrConfig) -> reltool_utils:call(Pid, {load_config, FilenameOrConfig}). -save_config(Pid, Filename, InclDefaults, InclDerivates) -> - reltool_utils:call(Pid, {save_config, Filename, InclDefaults, InclDerivates}). +save_config(Pid, Filename, InclDef, InclDeriv) -> + reltool_utils:call(Pid, {save_config, Filename, InclDef, InclDeriv}). reset_config(Pid) -> reltool_utils:call(Pid, reset_config). @@ -128,18 +132,20 @@ init(Options) -> end. do_init(Options) -> - case parse_options(Options) of - {#state{parent_pid = ParentPid, common = C, sys = Sys} = S, Status} -> - %% 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), - case Status3 of - {ok, _Warnings} -> - loop(S3#state{status = Status3, old_status = {ok, []}}); - {error, Reason} -> - exit(Reason) - end + {S, Status} = parse_options(Options), + #state{parent_pid = ParentPid, common = C, sys = Sys} = S, + + %% 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), + case Status3 of + {ok, _Warnings} -> % BUGBUG: handle warnings + loop(S3#state{status = Status3, old_status = {ok, []}}); + {error, Reason} -> + exit(Reason) end. parse_options(Opts) -> @@ -156,15 +162,28 @@ parse_options(Opts) -> rels = reltool_utils:default_rels(), emu_name = ?DEFAULT_EMU_NAME, profile = ?DEFAULT_PROFILE, - incl_sys_filters = reltool_utils:decode_regexps(incl_sys_filters, ?DEFAULT_INCL_SYS_FILTERS, []), - excl_sys_filters = reltool_utils:decode_regexps(excl_sys_filters, ?DEFAULT_EXCL_SYS_FILTERS, []), - incl_app_filters = reltool_utils:decode_regexps(incl_app_filters, ?DEFAULT_INCL_APP_FILTERS, []), - excl_app_filters = reltool_utils:decode_regexps(excl_app_filters, ?DEFAULT_EXCL_APP_FILTERS, []), + 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, - app_type = ?DEFAULT_APP_TYPE, + rel_app_type = ?DEFAULT_REL_APP_TYPE, + embedded_app_type = ?DEFAULT_EMBEDDED_APP_TYPE, app_file = ?DEFAULT_APP_FILE, - incl_archive_filters = reltool_utils:decode_regexps(incl_archive_filters, ?DEFAULT_INCL_ARCHIVE_FILTERS, []), - excl_archive_filters = reltool_utils:decode_regexps(excl_archive_filters, ?DEFAULT_EXCL_ARCHIVE_FILTERS, []), + 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}, C2 = #common{sys_debug = [], @@ -176,6 +195,9 @@ parse_options(Opts) -> S = #state{options = Opts}, parse_options(Opts, S, C2, Sys, {ok, []}). +dec_re(Key, Regexps, Old) -> + reltool_utils:decode_regexps(Key, Regexps, Old). + parse_options([{Key, Val} | KeyVals], S, C, Sys, Status) -> case Key of parent -> @@ -194,30 +216,38 @@ parse_options([{Key, Val} | KeyVals], S, C, Sys, Status) -> parse_options(KeyVals, S, C, Sys2, Status2); _ -> Text = lists:flatten(io_lib:format("~p", [{Key, Val}])), - Status2 = reltool_utils:return_first_error(Status, "Illegal option: " ++ Text), + Status2 = + reltool_utils:return_first_error(Status, + "Illegal option: " ++ Text), parse_options(KeyVals, S, C, Sys, Status2) 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), + Status2 = reltool_utils:return_first_error(Status, + "Illegal options: " ++ Text), {S#state{common = C, sys = Sys}, Status2}. loop(#state{common = C, sys = Sys} = S) -> receive {system, From, Msg} -> - sys:handle_system_msg(Msg, From, S#state.parent_pid, ?MODULE, C#common.sys_debug, S); - {call, ReplyTo, Ref, {get_config, InclDefaults, InclDerivates}} -> - Reply = do_get_config(S, InclDefaults, InclDerivates), + sys:handle_system_msg(Msg, + From, + S#state.parent_pid, + ?MODULE, + C#common.sys_debug, + S); + {call, ReplyTo, Ref, {get_config, InclDef, InclDeriv}} -> + Reply = do_get_config(S, InclDef, InclDeriv), 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); - {call, ReplyTo, Ref, {save_config, Filename, InclDefaults, InclDerivates}} -> - Reply = do_save_config(S, Filename, InclDefaults, InclDerivates), + {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} -> @@ -225,43 +255,44 @@ loop(#state{common = C, sys = Sys} = S) -> S3 = shrink_sys(S2), {S4, Status2} = refresh(S3, true, Status), {S5, Status3} = analyse(S4#state{old_sys = S4#state.sys}, Status2), - S6 = + S6 = case Status3 of {ok, _Warnings} -> S5#state{status = Status3, old_status = S#state.status}; {error, _} -> + %% Keep old state S end, 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, + S2 = S#state{sys = S#state.old_sys, old_sys = S#state.sys, status = S#state.old_status, old_status = S#state.status}, ?MODULE:loop(S2); {call, ReplyTo, Ref, {get_rel, RelName}} -> Sys = S#state.sys, - Reply = + Reply = case lists:keysearch(RelName, #rel.name, Sys#sys.rels) of {value, Rel} -> - {ok, reltool_target:gen_rel(Rel, Sys)}; + reltool_target:gen_rel(Rel, Sys); false -> - {error, "No such release"} + {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 = + Reply = case lists:keysearch(RelName, #rel.name, Sys#sys.rels) of {value, Rel} -> PathFlag = true, - Variables = [], - reltool_target:gen_script(Rel, Sys, PathFlag, Variables); + Vars = [], + reltool_target:gen_script(Rel, Sys, PathFlag, Vars); false -> - {error, "No such release"} + {error, "No such release: " ++ RelName} end, reltool_utils:reply(ReplyTo, Ref, Reply), ?MODULE:loop(S); @@ -271,17 +302,18 @@ loop(#state{common = C, sys = Sys} = S) -> [M] -> {ok, M}; [] -> - {ok, missing_mod(ModName, ?MISSING_APP)} + {ok, missing_mod(ModName, ?MISSING_APP_NAME)} end, reltool_utils:reply(ReplyTo, Ref, Reply), ?MODULE:loop(S); {call, ReplyTo, Ref, {get_app, AppName}} when is_atom(AppName) -> - Reply = + Reply = case lists:keysearch(AppName, #app.name, Sys#sys.apps) of {value, App} -> {ok, App}; false -> - {error, enoent} + {error, "No such application: " ++ + atom_to_list(AppName)} end, reltool_utils:reply(ReplyTo, Ref, Reply), ?MODULE:loop(S); @@ -296,21 +328,22 @@ loop(#state{common = C, sys = Sys} = S) -> 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; {call, ReplyTo, Ref, {get_apps, Kind}} -> AppNames = case Kind of - whitelist -> + whitelist -> [A || A <- Sys#sys.apps, A#app.is_pre_included =:= true]; - blacklist -> + blacklist -> [A || A <- Sys#sys.apps, A#app.is_pre_included =:= false]; - source -> + source -> [A || A <- Sys#sys.apps, A#app.is_included =/= true, @@ -324,9 +357,10 @@ 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} = lists:foldl(fun(A, {X, Y}) -> do_set_app(X, A, Y) end, - {S, {ok, []}}, - Apps), + {S2, Status} = + lists:foldl(fun(A, {X, Y}) -> do_set_app(X, A, Y) end, + {S, {ok, []}}, + Apps), {S3, Status2} = analyse(S2, Status), reltool_utils:reply(ReplyTo, Ref, Status2), ?MODULE:loop(S3); @@ -335,26 +369,30 @@ loop(#state{common = C, sys = Sys} = S) -> ?MODULE:loop(S); {call, ReplyTo, Ref, {set_sys, Sys2}} -> S2 = S#state{sys = Sys2#sys{apps = Sys#sys.apps}}, - Force = + 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), - S6 = - case Status2 of - {ok, _Warnings} -> - S4#state{status = Status2, old_status = S#state.status}; - {error, _} -> - S - end, - reltool_utils:reply(ReplyTo, Ref, Status2), - ?MODULE:loop(S6); + {S4, Status2} = + analyse(S3#state{old_sys = S#state.sys}, Status), + {S5, Status3} = + case Status2 of + {ok, _Warnings} -> % BUGBUG: handle warnings + {S4#state{status = Status2, + old_status = S#state.status}, + Status2}; + {error, _} -> + %% Keep old state + {S, Status2} + end, + reltool_utils:reply(ReplyTo, Ref, Status3), + ?MODULE:loop(S5); {call, ReplyTo, Ref, get_status} -> reltool_utils:reply(ReplyTo, Ref, S#state.status), ?MODULE:loop(S); {call, ReplyTo, Ref, {gen_rel_files, Dir}} -> - Status = + Status = case reltool_target:gen_rel_files(S#state.sys, Dir) of ok -> {ok, []}; @@ -395,16 +433,25 @@ do_set_app(#state{sys = Sys} = S, App, Status) -> Sys2 = Sys#sys{apps = Apps2, escripts = Escripts}, {S#state{sys = Sys2}, Status2}. -analyse(#state{common = C, sys = #sys{apps = Apps0} = Sys} = S, Status) -> - Apps = lists:keydelete(?MISSING_APP, #app.name, Apps0), +analyse(#state{common = C, + sys = #sys{apps = Apps0, rels = Rels} = Sys} = S, + Status) -> + Apps = lists:keydelete(?MISSING_APP_NAME, #app.name, Apps0), 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, "missing"), + MissingApp = default_app(?MISSING_APP_NAME, "missing"), ets:insert(C#common.app_tab, MissingApp), - Apps2 = lists:map(fun(App) -> app_init_is_included(C, Sys, App) end, Apps), - Apps3 = + {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; @@ -412,25 +459,68 @@ analyse(#state{common = C, sys = #sys{apps = Apps0} = Sys} = S, Status) -> %% io:format("Missing mods: ~p\n", [MissingMods]), MissingApp2 = MissingApp#app{label = ?MISSING_APP_TEXT, info = missing_app_info(""), - mods = MissingMods, + mods = MissingMods, status = missing, uses_mods = []}, [MissingApp2 | Apps2] end, app_propagate_is_used_by(C, Apps3), Apps4 = read_apps(C, Sys, Apps3, []), - %% io:format("Missing app: ~p\n", [lists:keysearch(?MISSING_APP, #app.name, Apps4)]), + %% io:format("Missing app: ~p\n", + %% [lists:keysearch(?MISSING_APP_NAME, #app.name, Apps4)]), Sys2 = Sys#sys{apps = Apps4}, - try - Status2 = verify_config(Sys2, Status), - {S#state{sys = Sys2}, Status2} - catch - throw:{error, Status3} -> - {S, Status3} + + case verify_config(RelApps2, Sys2, Status3) of + {ok, _Warnings} = Status4 -> + {S#state{sys = Sys2}, Status4}; + {error, _} = Status4 -> + {S, Status4} end. -app_init_is_included(C, Sys, #app{mods = Mods} = A) -> - AppCond = +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). + +apps_in_rel(#rel{name = RelName, rel_apps = RelApps}, Apps, Status) -> + 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([{RelName, AppName} = RA | RelApps], Apps, Acc, Status) -> + case lists:member(RA, Acc) of + true -> + more_apps_in_rels(RelApps, Apps, Acc, Status); + 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); + 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) + end + end; +more_apps_in_rels([], _Apps, Acc, Status) -> + {Acc, Status}. + +app_init_is_included(C, + Sys, + #app{name = AppName, mods = Mods} = A, + RelApps, + Status) -> + AppCond = case A#app.incl_cond of undefined -> Sys#sys.incl_cond; _ -> A#app.incl_cond @@ -440,17 +530,37 @@ app_init_is_included(C, Sys, #app{mods = Mods} = A) -> undefined -> Sys#sys.mod_cond; _ -> A#app.mod_cond end, - IsIncl = - case AppCond of - include -> true; - exclude -> false; - derived -> undefined + Rels = [RelName || {RelName, AN} <- RelApps, AN =:= AppName], + {Default, IsPreIncl, IsIncl, Status2} = + case {AppCond, Rels} of + {include, _} -> + {undefined, true, true, Status}; + {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}; + {derived, []} -> + {undefined, undefined, undefined, Status}; + {derived, [_ | _]} -> % App is included in at least one rel + {true, undefined, true, Status} end, - A2 = A#app{is_pre_included = IsIncl, is_included = IsIncl}, + A2 = A#app{is_pre_included = IsPreIncl, + is_included = IsIncl, + rels = Rels}, ets:insert(C#common.app_tab, A2), - lists:foreach(fun(Mod) -> mod_init_is_included(C, Mod, ModCond, AppCond, undefined) end, Mods), - %%app_mod_init_is_included(C, AppName, Info, ModCond, AppCond), - A2. + lists:foreach(fun(Mod) -> + mod_init_is_included(C, + Mod, + ModCond, + AppCond, + Default) + end, + Mods), + {A2, Status2}. mod_init_is_included(C, M, ModCond, AppCond, Default) -> %% print(M#mod.name, hipe, "incl_cond -> ~p\n", [AppCond]), @@ -463,7 +573,8 @@ mod_init_is_included(C, M, ModCond, AppCond, Default) -> exclude -> false; undefined -> - %% print(M#mod.name, hipe, "mod_cond -> ~p\n", [ModCond]), + %% print(M#mod.name, hipe, "mod_cond -> ~p\n", + %% [ModCond]), case ModCond of all -> true; app -> false_to_undefined(M#mod.is_app_mod); @@ -493,7 +604,7 @@ false_to_undefined(Bool) -> false -> undefined; _ -> 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); @@ -502,9 +613,11 @@ app_propagate_is_included(_C, _Sys, [], Acc) -> mod_propagate_is_included(C, Sys, A, [#mod{name = ModName} | Mods], Acc) -> [M2] = ets:lookup(C#common.mod_tab, ModName), - %% 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]), - Acc2 = + %% 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]), + Acc2 = case M2#mod.is_included of true -> %% Propagate include mark @@ -519,11 +632,13 @@ mod_propagate_is_included(_C, _Sys, _A, [], Acc) -> Acc. mod_mark_is_included(C, Sys, UsedByName, [ModName | ModNames], Acc) -> - Acc3 = + 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]), + [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 @@ -533,19 +648,22 @@ mod_mark_is_included(C, Sys, UsedByName, [ModName | ModNames], Acc) -> Acc; undefined -> %% Mark and propagate - M2 = + M2 = case M#mod.incl_cond of include -> - M#mod{is_pre_included = true, is_included = true}; + M#mod{is_pre_included = true, + is_included = true}; exclude -> - M#mod{is_pre_included = true, is_included = true}; + M#mod{is_pre_included = true, + is_included = true}; undefined -> 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]), + %% 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 = + Acc2 = case A#app.is_included of true -> Acc; @@ -557,7 +675,7 @@ mod_mark_is_included(C, Sys, UsedByName, [ModName | ModNames], Acc) -> undefined -> Sys#sys.mod_cond; _ -> A#app.mod_cond end, - Filter = + Filter = fun(M3) -> case ModCond of all -> true; @@ -569,15 +687,25 @@ mod_mark_is_included(C, Sys, UsedByName, [ModName | ModNames], Acc) -> 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]]), + %% [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, Sys, ModName, [M3#mod.name || M3 <- Mods], Acc) + ets:insert(C#common.app_tab, A2), + mod_mark_is_included(C, + Sys, + ModName, + [M3#mod.name || + M3 <- Mods], + Acc) end, - mod_mark_is_included(C, Sys, ModName, M2#mod.uses_mods, Acc2) + mod_mark_is_included(C, + Sys, + ModName, + M2#mod.uses_mods, + Acc2) end; [] -> - M = missing_mod(ModName, ?MISSING_APP), + 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}), @@ -588,7 +716,7 @@ 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 of + case Name =:= ?MISSING_APP_NAME of true -> ok; false -> ok end, @@ -614,21 +742,22 @@ mod_propagate_is_used_by(_C, []) -> read_apps(C, Sys, [#app{mods = Mods, is_included = IsIncl} = A | Apps], Acc) -> {Mods2, IsIncl2} = read_apps(C, Sys, A, Mods, [], IsIncl), - %% reltool_utils:print(A#app.name, stdlib, "Mods2: ~p\n", [[M#mod.status || M <- Mods2]]), - Status = - case lists:keysearch(missing, #mod.status, Mods2) of - {value, _} -> missing; - false -> ok + Status = + case lists:keymember(missing, #mod.status, Mods2) of + true -> missing; + false -> ok end, 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)], + UsesApps = [M#mod.app_name || ModName <- UsesMods2, + M <- ets:lookup(C#common.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)], + UsedByApps = [M#mod.app_name || ModName <- UsedByMods2, + M <- ets:lookup(C#common.mod_tab, ModName)], UsedByApps2 = lists:usort(UsedByApps), - + A2 = A#app{mods = Mods2, status = Status, uses_mods = UsesMods2, @@ -644,12 +773,14 @@ read_apps(C, Sys, A, [#mod{name = ModName} | Mods], Acc, IsIncl) -> [M2] = ets:lookup(C#common.mod_tab, ModName), Status = do_get_status(M2), %% print(M2#mod.name, hipe, "status -> ~p\n", [Status]), - {IsIncl2, M3} = + {IsIncl2, M3} = case M2#mod.is_included of true -> - UsedByMods = [N || {_, N} <- ets:lookup(C#common.mod_used_by_tab, ModName)], + UsedByMods = + [N || {_, N} <- ets:lookup(C#common.mod_used_by_tab, + ModName)], {true, M2#mod{status = Status, used_by_mods = UsedByMods}}; - _ -> + _ -> {IsIncl, M2#mod{status = Status, used_by_mods = []}} end, ets:insert(C#common.mod_tab, M3), @@ -669,14 +800,12 @@ 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) -> +filter_app(A) -> Mods = [M#mod{is_app_mod = undefined, is_ebin_mod = undefined, uses_mods = undefined, - exists = false, - is_pre_included = undefined, - is_included = undefined} || - M <- A#app.mods, + exists = false} || + M <- A#app.mods, M#mod.incl_cond =/= undefined], if A#app.is_escript -> @@ -684,30 +813,21 @@ filter_app(A) -> label = undefined, info = undefined, mods = [], - uses_mods = undefined, - is_included = undefined}}; + uses_mods = undefined}}; Mods =:= [], A#app.mod_cond =:= undefined, A#app.incl_cond =:= undefined, A#app.use_selected_vsn =:= undefined -> false; true -> - {Dir, Dirs} = + {Dir, Dirs, OptVsn} = case A#app.use_selected_vsn of - undefined -> - {shrinked, []}; + undefined -> + {shrinked, [], undefined}; false -> - {shrinked, []}; + {shrinked, [], undefined}; true -> - {A#app.active_dir, [A#app.active_dir]}; - _ when A#app.is_escript -> - {A#app.active_dir, [A#app.active_dir]} - end, - OptVsn = - case A#app.use_selected_vsn of - undefined -> undefined; - false -> undefined; - true -> A#app.vsn + {A#app.active_dir, [A#app.active_dir], A#app.vsn} end, {true, A#app{active_dir = Dir, sorted_dirs = Dirs, @@ -715,8 +835,7 @@ filter_app(A) -> label = undefined, info = undefined, mods = Mods, - uses_mods = undefined, - is_included = undefined}} + uses_mods = undefined}} end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -730,37 +849,45 @@ refresh_app(#app{name = AppName, Status) -> if Force; OptLabel =:= undefined -> - {AppInfo, EbinMods, Status3} = + {AppInfo, EbinMods, Status3} = case IsEscript of false -> - + %% Add info from .app file Base = get_base(AppName, ActiveDir), {_, DefaultVsn} = reltool_utils:split_app_name(Base), Ebin = filename:join([ActiveDir, "ebin"]), - AppFile = filename:join([Ebin, atom_to_list(AppName) ++ ".app"]), - {AI, Status2} = read_app_info(AppFile, AppFile, AppName, DefaultVsn, Status), + AppFile = + filename:join([Ebin, + atom_to_list(AppName) ++ ".app"]), + {AI, Status2} = + read_app_info(AppFile, + AppFile, + AppName, + DefaultVsn, + Status), {AI, read_ebin_mods(Ebin, AppName), Status2}; true -> {App#app.info, Mods, Status} end, - + %% Add non-existing modules + AppInfoMods = AppInfo#app_info.modules, AppModNames = case AppInfo#app_info.mod of {StartModName, _} -> - case lists:member(StartModName, AppInfo#app_info.modules) of - true -> AppInfo#app_info.modules; - false -> [StartModName | AppInfo#app_info.modules] + case lists:member(StartModName, AppInfoMods) of + true -> AppInfoMods; + false -> [StartModName | AppInfoMods] end; - undefined -> - AppInfo#app_info.modules + undefined -> + AppInfoMods end, MissingMods = add_missing_mods(AppName, EbinMods, AppModNames), - + %% Add optional user config for each module Mods2 = add_mod_config(MissingMods ++ EbinMods, Mods), - + %% Set app flag for each module in app file Mods3 = set_mod_flags(Mods2, AppModNames), AppVsn = AppInfo#app_info.vsn, @@ -770,7 +897,7 @@ refresh_app(#app{name = AppName, _ -> atom_to_list(AppName) ++ "-" ++ AppVsn end, App2 = App#app{vsn = AppVsn, - label = AppLabel, + label = AppLabel, info = AppInfo, mods = lists:keysort(#mod.name, Mods3)}, {App2, Status3}; @@ -790,30 +917,59 @@ 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]), - {missing_app_info(DefaultVsn), reltool_utils:add_warning(Status, Text)}; - {error, Text} when Text =:= EnoentText-> - {missing_app_info(DefaultVsn), Status}; + 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)}; + {error, Text} when Text =:= EnoentText -> + Text2 = lists:concat([AppName, + ": Missing app file ", AppFile, "."]), + {missing_app_info(DefaultVsn), + reltool_utils:add_warning(Status, Text2)}; {error, Text} -> - Text2 = lists:concat([AppName, ": Cannot parse app file ", AppFile, " (", Text, ")."]), - {missing_app_info(DefaultVsn), reltool_utils:add_warning(Status, Text2)} + Text2 = lists:concat([AppName, + ": Cannot parse app file ", + AppFile, " (", Text, ")."]), + {missing_app_info(DefaultVsn), + reltool_utils:add_warning(Status, Text2)} end. parse_app_info(File, [{Key, Val} | KeyVals], AI, Status) -> case Key of - description -> parse_app_info(File, KeyVals, AI#app_info{description = Val}, Status); - id -> parse_app_info(File, KeyVals, AI#app_info{id = Val}, Status); - vsn -> parse_app_info(File, KeyVals, AI#app_info{vsn = Val}, Status); - modules -> parse_app_info(File, KeyVals, AI#app_info{modules = Val}, Status); - maxP -> parse_app_info(File, KeyVals, AI#app_info{maxP = Val}, Status); - maxT -> parse_app_info(File, KeyVals, AI#app_info{maxT = Val}, Status); - registered -> parse_app_info(File, KeyVals, AI#app_info{registered = Val}, Status); - included_applications -> parse_app_info(File, KeyVals, AI#app_info{incl_apps = Val}, Status); - applications -> parse_app_info(File, KeyVals, AI#app_info{applications = Val}, Status); - env -> parse_app_info(File, KeyVals, AI#app_info{env = Val}, Status); - mod -> parse_app_info(File, KeyVals, AI#app_info{mod = Val}, Status); - start_phases -> parse_app_info(File, KeyVals, AI#app_info{start_phases = Val}, Status); - _ -> parse_app_info(File, KeyVals, AI, reltool_utils:add_warning(Status, lists:concat(["Unexpected item ", Key, "in app file ", File]))) + description -> + parse_app_info(File, KeyVals, AI#app_info{description = Val}, + Status); + id -> + parse_app_info(File, KeyVals, AI#app_info{id = Val}, Status); + vsn -> + parse_app_info(File, KeyVals, AI#app_info{vsn = Val}, Status); + modules -> + parse_app_info(File, KeyVals, AI#app_info{modules = Val}, Status); + maxP -> + parse_app_info(File, KeyVals, AI#app_info{maxP = Val}, Status); + maxT -> + parse_app_info(File, KeyVals, AI#app_info{maxT = Val}, Status); + registered -> + parse_app_info(File, KeyVals, AI#app_info{registered = Val}, + Status); + included_applications -> + parse_app_info(File, KeyVals, AI#app_info{incl_apps = Val}, Status); + applications -> + parse_app_info(File, KeyVals, AI#app_info{applications = Val}, + Status); + env -> + parse_app_info(File, KeyVals, AI#app_info{env = Val}, Status); + mod -> + parse_app_info(File, KeyVals, AI#app_info{mod = Val}, Status); + start_phases -> + 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)) end; parse_app_info(_, [], AI, Status) -> {AI, Status}. @@ -824,7 +980,7 @@ read_ebin_mods(Ebin, AppName) -> Ext = code:objfile_extension(), InitMod = fun(F) -> File = filename:join([Ebin, F]), - init_mod(AppName, File, File, Ext) + init_mod(AppName, File, File, Ext) end, Files2 = [F || F <- Files, filename:extension(F) =:= Ext], pmap(InitMod, Files2); @@ -839,7 +995,7 @@ pmap(Fun, List) -> %% -record(pmap_res, {count, ref, res}). %% -record(pmap_wait, {count, ref, pid}). -%% +%% %% pmap(Fun, [H | T], N, Max, Count, WaitFor, Results) when N < Max -> %% Ref = make_ref(), %% Parent = self(), @@ -923,8 +1079,8 @@ missing_mod(ModName, AppName) -> add_mod_config(Mods, ModConfigs) -> AddConfig = fun(Config, Acc) -> - case lists:keysearch(Config#mod.name, #mod.name, Mods) of - {value, M} -> + case lists:keyfind(Config#mod.name, #mod.name, Mods) of + #mod{} = M -> M2 = M#mod{incl_cond = Config#mod.incl_cond}, lists:keystore(Config#mod.name, #mod.name, Acc, M2); false -> @@ -943,16 +1099,16 @@ set_mod_flags(Mods, AppModNames) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -do_get_config(S, InclDefaults, InclDerivates) -> +do_get_config(S, InclDef, InclDeriv) -> S2 = - case InclDerivates of + case InclDeriv of false -> shrink_sys(S); true -> S end, - {ok, reltool_target:gen_config(S2#state.sys, InclDefaults)}. + reltool_target:gen_config(S2#state.sys, InclDef). -do_save_config(S, Filename, InclDefaults, InclDerivates) -> - {ok, Config} = do_get_config(S, InclDefaults, InclDerivates), +do_save_config(S, Filename, InclDef, InclDeriv) -> + {ok, Config} = do_get_config(S, InclDef, InclDeriv), IoList = io_lib:format("%% config generated at ~w ~w\n~p.\n\n", [date(), time(), Config]), file:write_file(Filename, IoList). @@ -963,17 +1119,20 @@ 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(ShrinkedSys#sys{apps = []}, 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 = + {S3, Status3} = + analyse(S2#state{sys = MergedSys, old_sys = OldSys}, Status2), + S4 = case Status3 of {ok, _Warnings2} -> S3#state{status = Status3, old_status = S#state.status}; {error, _} -> + %% Keep old state S end, {S4, Status3}; @@ -988,37 +1147,51 @@ read_config(OldSys, Filename, Status) when is_list(Filename) -> read_config(OldSys, SysConfig, Status); {ok, Content} -> Text = lists:flatten(io_lib:format("~p", [Content])), - {OldSys, reltool_utils:return_first_error(Status, "Illegal file content: " ++ Text)}; + {OldSys, + reltool_utils:return_first_error(Status, + "Illegal file content: " ++ + Text)}; {error, Reason} -> Text = file:format_error(Reason), - {OldSys, reltool_utils:return_first_error(Status, "File access: " ++ Text)} + {OldSys, + reltool_utils:return_first_error(Status, + "Illegal config file " ++ + Filename ++ ": " ++ Text)} end; read_config(OldSys, {sys, KeyVals}, Status) -> {NewSys, Status2} = - try - decode(OldSys#sys{apps = [], rels = []}, KeyVals, Status) - catch - throw:{error, Text} -> - {OldSys, reltool_utils:return_first_error(Status, Text)} - end, - 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:keysearch(NewSys2#sys.boot_rel, #rel.name, NewSys2#sys.rels) of - {value, _} -> - {NewSys2, Status2}; - false -> - Text2 = "Missing rel: " ++ NewSys2#sys.boot_rel, - {OldSys, reltool_utils:return_first_error(Status2, Text2)} + 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} end; read_config(OldSys, BadConfig, Status) -> Text = lists:flatten(io_lib:format("~p", [BadConfig])), - {OldSys, reltool_utils:return_first_error(Status, "Illegal content: " ++ Text)}. + {OldSys, + reltool_utils:return_first_error(Status, "Illegal content: " ++ Text)}. -decode(#sys{apps = Apps} = Sys, [{erts = Name, AppKeyVals} | SysKeyVals], Status) +decode(#sys{apps = Apps} = Sys, [{erts = Name, AppKeyVals} | SysKeyVals], + Status) when is_atom(Name), is_list(AppKeyVals) -> App = default_app(Name), {App2, Status2} = decode(App, AppKeyVals, Status), @@ -1028,7 +1201,8 @@ decode(#sys{apps = Apps} = Sys, [{app, Name, AppKeyVals} | SysKeyVals], Status) App = default_app(Name), {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) +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), @@ -1038,173 +1212,229 @@ decode(#sys{apps = Apps, escripts = Escripts} = Sys, [{escript, File, AppKeyVals active_dir = File, sorted_dirs = [File]}, {App3, Status2} = decode(App2, AppKeyVals, Status), - decode(Sys#sys{apps = [App3 | Apps], escripts = [File | Escripts]}, SysKeyVals, Status2); -decode(#sys{rels = Rels} = Sys, [{rel, Name, Vsn, RelApps} | SysKeyVals], Status) + decode(Sys#sys{apps = [App3 | Apps], escripts = [File | Escripts]}, + SysKeyVals, + Status2); +decode(#sys{rels = Rels} = Sys, [{rel, Name, Vsn, RelApps} | SysKeyVals], + Status) 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} = + {Sys3, Status3} = case Key of root_dir when is_list(Val) -> {Sys#sys{root_dir = Val}, Status}; lib_dirs when is_list(Val) -> {Sys#sys{lib_dirs = Val}, Status}; - mod_cond when Val =:= all; Val =:= app; - Val =:= ebin; Val =:= derived; - Val =:= none -> + mod_cond when Val =:= all; + Val =:= app; + Val =:= ebin; + Val =:= derived; + Val =:= none -> {Sys#sys{mod_cond = Val}, Status}; - incl_cond when Val =:= include; Val =:= exclude; - Val =:= derived -> + incl_cond when Val =:= include; + Val =:= exclude; + Val =:= derived -> {Sys#sys{incl_cond = Val}, Status}; boot_rel when is_list(Val) -> {Sys#sys{boot_rel = Val}, Status}; emu_name when is_list(Val) -> {Sys#sys{emu_name = Val}, Status}; - profile when Val =:= development -> - Val = ?DEFAULT_PROFILE, % assert, - {Sys#sys{profile = Val, - incl_sys_filters = reltool_utils:decode_regexps(incl_sys_filters, - ?DEFAULT_INCL_SYS_FILTERS, - Sys#sys.incl_sys_filters), - excl_sys_filters = reltool_utils:decode_regexps(excl_sys_filters, - ?DEFAULT_EXCL_SYS_FILTERS, - Sys#sys.excl_sys_filters), - incl_app_filters = reltool_utils:decode_regexps(incl_app_filters, - ?DEFAULT_INCL_APP_FILTERS, - Sys#sys.incl_app_filters), - excl_app_filters = reltool_utils:decode_regexps(excl_app_filters, - ?DEFAULT_EXCL_APP_FILTERS, - Sys#sys.excl_app_filters)}, - Status}; - profile when Val =:= embedded -> - {Sys#sys{profile = Val, - incl_sys_filters = reltool_utils:decode_regexps(incl_sys_filters, - ?EMBEDDED_INCL_SYS_FILTERS, - Sys#sys.incl_sys_filters), - excl_sys_filters = reltool_utils:decode_regexps(excl_sys_filters, - ?EMBEDDED_EXCL_SYS_FILTERS, - Sys#sys.excl_sys_filters), - incl_app_filters = reltool_utils:decode_regexps(incl_app_filters, - ?EMBEDDED_INCL_APP_FILTERS, - Sys#sys.incl_app_filters), - excl_app_filters = reltool_utils:decode_regexps(excl_app_filters, - ?EMBEDDED_EXCL_APP_FILTERS, - Sys#sys.excl_app_filters)}, - Status}; - profile when Val =:= standalone -> - {Sys#sys{profile = Val, - incl_sys_filters = reltool_utils:decode_regexps(incl_sys_filters, - ?STANDALONE_INCL_SYS_FILTERS, - Sys#sys.incl_sys_filters), - excl_sys_filters = reltool_utils:decode_regexps(excl_sys_filters, - ?STANDALONE_EXCL_SYS_FILTERS, - Sys#sys.excl_sys_filters), - incl_app_filters = reltool_utils:decode_regexps(incl_app_filters, - ?STANDALONE_INCL_APP_FILTERS, - Sys#sys.incl_app_filters), - excl_app_filters = reltool_utils:decode_regexps(excl_app_filters, - ?STANDALONE_EXCL_APP_FILTERS, - Sys#sys.excl_app_filters)}, + profile when Val =:= development; + Val =:= embedded; + Val =:= standalone -> + InclSys = reltool_utils:choose_default(incl_sys_filters, Val, false), + ExclSys = reltool_utils:choose_default(excl_sys_filters, Val, false), + 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}; incl_sys_filters -> - {Sys#sys{incl_sys_filters = reltool_utils:decode_regexps(Key, Val, Sys#sys.incl_sys_filters)}, Status}; + {Sys#sys{incl_sys_filters = + dec_re(Key, + Val, + Sys#sys.incl_sys_filters)}, + Status}; excl_sys_filters -> - {Sys#sys{excl_sys_filters = reltool_utils:decode_regexps(Key, Val, Sys#sys.excl_sys_filters)}, Status}; + {Sys#sys{excl_sys_filters = + dec_re(Key, + Val, + Sys#sys.excl_sys_filters)}, + Status}; incl_app_filters -> - {Sys#sys{incl_app_filters = reltool_utils:decode_regexps(Key, Val, Sys#sys.incl_app_filters)}, Status}; + {Sys#sys{incl_app_filters = + dec_re(Key, + Val, + Sys#sys.incl_app_filters)}, + Status}; excl_app_filters -> - {Sys#sys{excl_app_filters = reltool_utils:decode_regexps(Key, Val, Sys#sys.excl_app_filters)}, Status}; + {Sys#sys{excl_app_filters = + dec_re(Key, + Val, + Sys#sys.excl_app_filters)}, + Status}; incl_archive_filters -> - {Sys#sys{incl_archive_filters = reltool_utils:decode_regexps(Key, Val, Sys#sys.incl_archive_filters)}, Status}; + {Sys#sys{incl_archive_filters = + dec_re(Key, + Val, + Sys#sys.incl_archive_filters)}, + Status}; excl_archive_filters -> - {Sys#sys{excl_archive_filters = reltool_utils:decode_regexps(Key, Val, Sys#sys.excl_archive_filters)}, Status}; + {Sys#sys{excl_archive_filters = + dec_re(Key, + Val, + Sys#sys.excl_archive_filters)}, + Status}; archive_opts when is_list(Val) -> {Sys#sys{archive_opts = Val}, Status}; relocatable when Val =:= true; Val =:= false -> {Sys#sys{relocatable = Val}, Status}; - app_type when Val =:= permanent; Val =:= transient; Val =:= temporary; - Val =:= load; Val =:= none -> - {Sys#sys{app_type = Val}, Status}; - app_file when Val =:= keep; Val =:= strip, Val =:= all -> + rel_app_type when Val =:= permanent; + Val =:= transient; + Val =:= temporary; + Val =:= load; + Val =:= none -> + {Sys#sys{rel_app_type = Val}, Status}; + embedded_app_type when Val =:= permanent; + Val =:= transient; + Val =:= temporary; + Val =:= load; + Val =:= none; + Val =:= undefined -> + {Sys#sys{embedded_app_type = Val}, Status}; + app_file when Val =:= keep; Val =:= strip, Val =:= all -> {Sys#sys{app_file = Val}, Status}; - debug_info when Val =:= keep; Val =:= strip -> + debug_info when Val =:= keep; Val =:= strip -> {Sys#sys{debug_info = Val}, Status}; _ -> Text = lists:flatten(io_lib:format("~p", [{Key, Val}])), - {Sys, reltool_utils:return_first_error(Status, "Illegal option: " ++ Text)} + {Sys, reltool_utils:return_first_error(Status, + "Illegal option: " ++ + Text)} end, decode(Sys3, KeyVals, Status3); decode(#app{} = App, [{Key, Val} | KeyVals], Status) -> - {App2, Status2} = + {App2, Status2} = case Key of - mod_cond when Val =:= all; Val =:= app; Val =:= ebin; Val =:= derived; Val =:= none -> + mod_cond when Val =:= all; + Val =:= app; + Val =:= ebin; + Val =:= derived; + Val =:= none -> {App#app{mod_cond = Val}, Status}; - incl_cond when Val =:= include; Val =:= exclude; Val =:= derived -> + incl_cond when Val =:= include; + Val =:= exclude; + Val =:= derived -> {App#app{incl_cond = Val}, Status}; - debug_info when Val =:= keep; Val =:= strip -> + debug_info when Val =:= keep; + Val =:= strip -> {App#app{debug_info = Val}, Status}; - app_file when Val =:= keep; Val =:= strip, Val =:= all -> + app_file when Val =:= keep; + Val =:= strip; + Val =:= all -> {App#app{app_file = Val}, Status}; - app_type when Val =:= permanent; Val =:= transient; Val =:= temporary; - Val =:= load; Val =:= none -> + app_type when Val =:= permanent; + Val =:= transient; + Val =:= temporary; + Val =:= load; + Val =:= none; + Val =:= undefined -> {App#app{app_type = Val}, Status}; incl_app_filters -> - {App#app{incl_app_filters = reltool_utils:decode_regexps(Key, Val, App#app.incl_app_filters)}, Status}; + {App#app{incl_app_filters = + dec_re(Key, + Val, + App#app.incl_app_filters)}, + Status}; excl_app_filters -> - {App#app{excl_app_filters = reltool_utils:decode_regexps(Key, Val, App#app.excl_app_filters)}, Status}; + {App#app{excl_app_filters = + dec_re(Key, + Val, + App#app.excl_app_filters)}, + Status}; incl_archive_filters -> - {App#app{incl_archive_filters = reltool_utils:decode_regexps(Key, Val, App#app.incl_archive_filters)}, Status}; + {App#app{incl_archive_filters = + dec_re(Key, + Val, + App#app.incl_archive_filters)}, + Status}; excl_archive_filters -> - {App#app{excl_archive_filters = reltool_utils:decode_regexps(Key, Val, App#app.excl_archive_filters)}, Status}; + {App#app{excl_archive_filters = + dec_re(Key, + Val, + App#app.excl_archive_filters)}, + Status}; archive_opts when is_list(Val) -> {App#app{archive_opts = Val}, Status}; - vsn when is_list(Val) -> + vsn when is_list(Val) -> {App#app{use_selected_vsn = true, vsn = Val}, Status}; _ -> Text = lists:flatten(io_lib:format("~p", [{Key, Val}])), - {App, reltool_utils:return_first_error(Status, "Illegal option: " ++ Text)} + {App, reltool_utils:return_first_error(Status, + "Illegal option: " ++ Text)} end, decode(App2, KeyVals, Status2); -decode(#app{mods = Mods} = App, [{mod, Name, ModKeyVals} | AppKeyVals], Status) -> +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} = + {Mod2, Status2} = case Key of - incl_cond when Val =:= include; Val =:= exclude; Val =:= derived -> + incl_cond when Val =:= include; Val =:= exclude; Val =:= derived -> {Mod#mod{incl_cond = Val}, Status}; - debug_info when Val =:= keep; Val =:= strip -> + debug_info when Val =:= keep; Val =:= strip -> {Mod#mod{debug_info = Val}, Status}; _ -> Text = lists:flatten(io_lib:format("~p", [{Key, Val}])), - {Mod, reltool_utils:return_first_error(Status, "Illegal option: " ++ Text)} + {Mod, + reltool_utils:return_first_error(Status, + "Illegal option: " ++ Text)} end, decode(Mod2, KeyVals, Status2); decode(#rel{rel_apps = RelApps} = Rel, [RelApp | KeyVals], Status) -> - RA = + {ValidTypesAssigned, RA} = case RelApp of Name when is_atom(Name) -> - #rel_app{name = Name, app_type = undefined, incl_apps = []}; + {true, #rel_app{name = Name}}; {Name, Type} when is_atom(Name) -> - #rel_app{name = Name, app_type = Type, incl_apps = []}; + {is_type(Type), #rel_app{name = Name, app_type = Type}}; {Name, InclApps} when is_atom(Name), is_list(InclApps) -> - #rel_app{name = Name, app_type = undefined, incl_apps = InclApps}; + VI = lists:all(fun erlang:is_atom/1, InclApps), + {VI, #rel_app{name = Name, incl_apps = InclApps}}; {Name, Type, InclApps} when is_atom(Name), is_list(InclApps) -> - #rel_app{name = Name, app_type = Type, incl_apps = InclApps}; + VT = is_type(Type), + VI = lists:all(fun erlang:is_atom/1, InclApps), + {VT andalso VI, + #rel_app{name = Name, app_type = Type, incl_apps = InclApps}}; _ -> - #rel_app{incl_apps = []} + {false, #rel_app{incl_apps = []}} end, - IsType = is_type(RA#rel_app.app_type), - NonAtoms = [IA || IA <- RA#rel_app.incl_apps, not is_atom(IA)], - if - IsType, NonAtoms =:= [] -> + case ValidTypesAssigned of + true -> decode(Rel#rel{rel_apps = RelApps ++ [RA]}, KeyVals, Status); - true -> + false -> Text = lists:flatten(io_lib:format("~p", [RelApp])), - Status2 = reltool_utils:return_first_error(Status, "Illegal option: " ++ Text), + Status2 = + reltool_utils:return_first_error(Status, + "Illegal option: " ++ Text), decode(Rel, KeyVals, Status2) end; decode(Acc, [], Status) -> @@ -1227,7 +1457,7 @@ is_type(Type) -> split_escript_name(File) when is_list(File) -> Label = filename:basename(File, ".escript"), {list_to_atom("*escript* " ++ Label), Label}. - + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% refresh(#state{sys = Sys} = S, Force, Status) -> @@ -1245,7 +1475,8 @@ merge_config(OldSys, NewSys, Force, Status) -> 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), + {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, @@ -1253,54 +1484,59 @@ merge_config(OldSys, NewSys, Force, Status) -> apps = PatchedApps}, {NewSys2, Status5}. -verify_config(Sys, Status) -> - case lists:keymember(Sys#sys.boot_rel, #rel.name, Sys#sys.rels) of - true -> - lists:foreach(fun(Rel)-> check_rel(Rel, Sys, Status) end, Sys#sys.rels), - Status; +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([Sys#sys.boot_rel, ": release is mandatory"]), - Status2 = reltool_utils:return_first_error(Status, Text), - throw({error, Status2}) + Text = lists:concat(["Release ", BootRel, + " is mandatory (used as boot_rel)"]), + reltool_utils:return_first_error(Status, Text) + end. + +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. -check_rel(#rel{name = RelName, rel_apps = RelApps}, #sys{apps = Apps}, Status) -> +check_rel(RelName, RelApps, Status) -> EnsureApp = - fun(AppName) -> - case lists:keymember(AppName, #rel_app.name, RelApps) of + fun(AppName, Acc) -> + case lists:member({RelName, AppName}, RelApps) of true -> - ok; + Acc; false -> - Text = lists:concat([RelName, ": ", AppName, " is not included."]), - Status2 = reltool_utils:return_first_error(Status, Text), - throw({error, Status2}) - end - end, - EnsureApp(kernel), - EnsureApp(stdlib), - CheckRelApp = - fun(#rel_app{name = AppName}) -> - case lists:keysearch(AppName, #app.name, Apps) of - {value, App} when App#app.is_pre_included -> - ok; - {value, App} when App#app.is_included -> - ok; - _ -> - Text = lists:concat([RelName, ": uses application ", - AppName, " that not is included."]), - Status2 = reltool_utils:return_first_error(Status, Text), - %% throw BUGBUG: add throw - ({error, Status2}) + Text = lists:concat(["Mandatory application ", + AppName, + " is not included in release ", + RelName]), + reltool_utils:return_first_error(Acc, Text) end end, - lists:foreach(CheckRelApp, RelApps). + Mandatory = [kernel, stdlib], + lists:foldl(EnsureApp, Status, Mandatory). patch_erts_version(RootDir, Apps, Status) -> AppName = erts, - case lists:keysearch(AppName, #app.name, Apps) of - {value, Erts} -> + case lists:keyfind(AppName, #app.name, Apps) of + #app{vsn = Vsn} = Erts -> LocalRoot = code:root_dir(), - Vsn = Erts#app.vsn, if LocalRoot =:= RootDir, Vsn =:= "" -> Vsn2 = erlang:system_info(version), @@ -1308,13 +1544,14 @@ 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(Status, + "erts has no version")}; true -> {Apps, Status} end; false -> - Text = "erts cannnot be found in the root directory " ++ RootDir, - Status2 = reltool_utils:return_first_error(Status, Text), + Text = "erts cannot be found in the root directory " ++ RootDir, + Status2 = reltool_utils:return_first_error(Status, Text), {Apps, Status2} end. @@ -1327,21 +1564,31 @@ libs_to_dirs(RootDir, LibDirs, Status) -> [] -> Fun = fun(Base) -> AppDir = filename:join([RootLibDir, Base]), - case filelib:is_dir(filename:join([AppDir, "ebin"]), erl_prim_loader) of + case filelib:is_dir(filename:join([AppDir, + "ebin"]), + erl_prim_loader) of true -> AppDir; false -> - filename:join([RootDir, Base, "preloaded"]) + filename:join([RootDir, + Base, + "preloaded"]) end end, - ErtsFiles = [{erts, Fun(F)} || F <- RootFiles, lists:prefix("erts", F)], + ErtsFiles = [{erts, Fun(F)} || F <- RootFiles, + lists:prefix("erts", F)], app_dirs2(AllLibDirs, [ErtsFiles], Status); [Duplicate | _] -> - {[], reltool_utils:return_first_error(Status, "Duplicate library: " ++ Duplicate)} + {[], + reltool_utils:return_first_error(Status, + "Duplicate library: " ++ + Duplicate)} end; {error, Reason} -> Text = file:format_error(Reason), - {[], reltool_utils:return_first_error(Status, "Missing root library " ++ RootDir ++ ": " ++ Text)} + {[], reltool_utils:return_first_error(Status, + "Missing root library " ++ + RootDir ++ ": " ++ Text)} end. app_dirs2([Lib | Libs], Acc, Status) -> @@ -1352,8 +1599,9 @@ app_dirs2([Lib | Libs], Acc, Status) -> AppDir = filename:join([Lib, Base]), EbinDir = filename:join([AppDir, "ebin"]), case filelib:is_dir(EbinDir, erl_prim_loader) of - true -> - {Name, _Vsn} = reltool_utils:split_app_name(Base), + true -> + {Name, _Vsn} = + reltool_utils:split_app_name(Base), case Name of erts -> false; _ -> {true, {Name, AppDir}} @@ -1366,7 +1614,9 @@ app_dirs2([Lib | Libs], Acc, Status) -> app_dirs2(Libs, [Files2 | Acc], Status); {error, Reason} -> Text = file:format_error(Reason), - {[], reltool_utils:return_first_error(Status, "Illegal library " ++ Lib ++ ": " ++ Text)} + {[], reltool_utils:return_first_error(Status, + "Illegal library " ++ + Lib ++ ": " ++ Text)} end; app_dirs2([], Acc, Status) -> {lists:sort(lists:append(Acc)), Status}. @@ -1380,17 +1630,29 @@ escripts_to_apps([Escript | Escripts], Apps, OldApps, Status) -> [AppLabel, "ebin", File] -> case filename:extension(File) of ".app" -> - {AppName, DefaultVsn} = reltool_utils:split_app_name(AppLabel), - AppFileName = filename:join([Escript, FullName]), + {AppName, DefaultVsn} = + reltool_utils:split_app_name(AppLabel), + AppFileName = + filename:join([Escript, FullName]), {Info, StatusAcc2} = - read_app_info(GetBin(), AppFileName, AppName, DefaultVsn, Status), + read_app_info(GetBin(), + AppFileName, + AppName, + DefaultVsn, + Status), Dir = filename:join([Escript, AppName]), - {[{AppName, app, Dir, Info} | FileAcc], StatusAcc2}; + {[{AppName, app, Dir, Info} | FileAcc], + StatusAcc2}; E when E =:= Ext -> - {AppName, _} = reltool_utils:split_app_name(AppLabel), - Mod = init_mod(AppName, File, {File, GetBin()}, Ext), + {AppName, _} = + reltool_utils:split_app_name(AppLabel), + Mod = init_mod(AppName, + File, + {File, GetBin()}, + Ext), Dir = filename:join([Escript, AppName]), - {[{AppName, mod, Dir, Mod} | FileAcc], StatusAcc}; + {[{AppName, mod, Dir, Mod} | FileAcc], + StatusAcc}; _ -> {FileAcc, StatusAcc} end; @@ -1398,13 +1660,21 @@ escripts_to_apps([Escript | Escripts], Apps, OldApps, Status) -> Bin = GetBin(), {ok, {ModName, _}} = beam_lib:version(Bin), ModStr = atom_to_list(ModName) ++ Ext, - Mod = init_mod(EscriptAppName, ModStr, {ModStr, GetBin()}, Ext), - {[{EscriptAppName, mod, Escript, Mod} | FileAcc], StatusAcc}; + Mod = init_mod(EscriptAppName, + ModStr, + {ModStr, GetBin()}, + Ext), + {[{EscriptAppName, mod, Escript, Mod} | FileAcc], + StatusAcc}; [File] -> case filename:extension(File) of E when E =:= Ext -> - Mod = init_mod(EscriptAppName, File, {File, GetBin()}, Ext), - {[{EscriptAppName, mod, File, Mod} | FileAcc], StatusAcc}; + Mod = init_mod(EscriptAppName, + File, + {File, GetBin()}, + Ext), + {[{EscriptAppName, mod, File, Mod} | FileAcc], + StatusAcc}; _ -> {FileAcc, StatusAcc} end; @@ -1412,71 +1682,111 @@ escripts_to_apps([Escript | Escripts], Apps, OldApps, Status) -> {FileAcc, StatusAcc} end end, - try - case escript:foldl(Fun, {[], Status}, Escript) of - {ok, {Files, Status2}} -> - {Apps2, Status3} = files_to_apps(Escript, lists:sort(Files), Apps, Apps, OldApps, Status2), - escripts_to_apps(Escripts, Apps2, OldApps, Status3); - {error, Reason} -> - Text = lists:flatten(io_lib:format("~p", [Reason])), - {[], reltool_utils:return_first_error(Status, "Illegal escript " ++ Escript ++ ": " ++ Text)} - end - catch - throw:Reason2 when is_list(Reason2) -> - {[], reltool_utils:return_first_error(Status, "Illegal escript " ++ Escript ++ ": " ++ Reason2)} + case reltool_utils:escript_foldl(Fun, {[], Status}, Escript) of + {ok, {Files, Status2}} -> + {Apps2, Status3} = + files_to_apps(Escript, + lists:sort(Files), + Apps, + Apps, + OldApps, + Status2), + escripts_to_apps(Escripts, Apps2, OldApps, 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) -> {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) -> +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); + {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); + files_to_apps(Escript, + Files, + [App2 | Acc2], + Apps, + OldApps, + Status); [App | Acc2] -> - PrevApp = App#app{mods = lists:keysort(#mod.name, App#app.mods)}, + 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) + {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), + {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) -> - case lists:keysearch(AppName, #app.name, OldApps) of - {value, App} -> - ok; - false -> - App = default_app(AppName, Dir) - end, - App2 = App#app{is_escript = true, - label = filename:basename(Dir, ".escript"), - info = Info, - mods = Mods, - active_dir = Dir, - sorted_dirs = [Dir]}, - case lists:keysearch(AppName, #app.name, Apps) of - {value, _} -> + App1 = case lists:keyfind(AppName, #app.name, OldApps) of + #app{} = App -> + App; + false -> + default_app(AppName, Dir) + end, + App2 = App1#app{is_escript = true, + label = filename:basename(Dir, ".escript"), + info = Info, + mods = Mods, + active_dir = Dir, + sorted_dirs = [Dir]}, + case lists:keymember(AppName, #app.name, Apps) of + true -> Error = lists:concat([AppName, ": Application name clash. ", - "Escript ", Dir," contains application ", AppName, "."]), + "Escript ", Dir," contains application ", + AppName, "."]), {App2, reltool_utils:return_first_error(Status, Error)}; false -> {App2, Status} end. -merge_app_dirs([{Name, Dir} | Rest], [App | Apps], OldApps) +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]}, @@ -1485,25 +1795,26 @@ merge_app_dirs([{Name, Dir} | Rest], Apps, OldApps) -> %% Initate app Apps2 = sort_app_dirs(Apps), Apps4 = - case lists:keysearch(Name, #app.name, Apps) of + case lists:keyfind(Name, #app.name, Apps) of false -> - case lists:keysearch(Name, #app.name, OldApps) of - {value, OldApp} when OldApp#app.active_dir =:= Dir -> + case lists:keyfind(Name, #app.name, OldApps) of + false -> + App = default_app(Name, Dir), + [App | Apps2]; + #app{active_dir = Dir} = OldApp -> [OldApp | Apps2]; - {value, OldApp} -> - App = + OldApp -> + App = case filter_app(OldApp) of {true, NewApp} -> - NewApp#app{active_dir = Dir, sorted_dirs = [Dir]}; + NewApp#app{active_dir = Dir, + sorted_dirs = [Dir]}; false -> default_app(Name, Dir) end, - [App | Apps2]; - false -> - App = default_app(Name, Dir), [App | Apps2] end; - {value, OldApp} -> + OldApp -> Apps3 = lists:keydelete(Name, #app.name, Apps2), App = OldApp#app{sorted_dirs = [Dir | OldApp#app.sorted_dirs]}, [App | Apps3] @@ -1545,12 +1856,14 @@ default_app(Name) -> status = missing, uses_mods = undefined, is_pre_included = undefined, - is_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 -> +%% 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 = + OptLabel = case Info#app_info.vsn =:= New#app.vsn of true -> New#app.label; false -> undefined % Cause refresh @@ -1559,23 +1872,28 @@ refresh_apps([Old | OldApps], [New | NewApps], Acc, Force, Status) when New#app. refresh_app(New#app{label = OptLabel, active_dir = ActiveDir, vsn = Info#app_info.vsn, - info = Info}, + 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 -> +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 -> +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 of + 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."]), + 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); @@ -1586,25 +1904,32 @@ refresh_apps([], [New | NewApps], Acc, Force, Status) -> refresh_apps([Old | OldApps], [], Acc, Force, Status) -> %% No new version. Remove the old. Status2 = - case Old#app.name =:= ?MISSING_APP of + 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."]), + 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) -> {lists:reverse(Acc), Status}. -ensure_app_info(#app{is_escript = true, active_dir = Dir, info = Info}, Status) -> +ensure_app_info(#app{is_escript = true, active_dir = Dir, info = Info}, + Status) -> {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, vsn = Vsn, sorted_dirs = Dirs, info = undefined}, Status) -> +ensure_app_info(#app{name = Name, + vsn = Vsn, + sorted_dirs = Dirs, + info = undefined}, + Status) -> ReadInfo = fun(Dir, StatusAcc) -> Base = get_base(Name, Dir), @@ -1621,8 +1946,10 @@ ensure_app_info(#app{name = Name, vsn = Vsn, sorted_dirs = Dirs, info = undefine %% No redundant info Status2; [BadVsn | _] -> - Error2 = lists:concat([Name, ": Application version clash. ", - "Multiple directories contains version \"", BadVsn, "\"."]), + Error2 = + lists:concat([Name, ": Application version clash. ", + "Multiple directories contains version \"", + BadVsn, "\"."]), reltool_utils:return_first_error(Status2, Error2) end, FirstInfo = hd(AllInfo), @@ -1637,7 +1964,11 @@ ensure_app_info(#app{name = Name, vsn = Vsn, sorted_dirs = Dirs, info = undefine {Info, VsnDir} -> {Info, VsnDir, Status3}; false -> - Error3 = lists:concat([Name, ": No application directory contains selected version \"", Vsn, "\"."]), + Error3 = + lists:concat([Name, + ": No application directory contains ", + "selected version \"", + Vsn, "\"."]), Status4 = reltool_utils:return_first_error(Status3, Error3), {FirstInfo, FirstDir, Status4} end diff --git a/lib/reltool/src/reltool_sys_win.erl b/lib/reltool/src/reltool_sys_win.erl index ea80ab7e85..dbb8e32aa2 100644 --- a/lib/reltool/src/reltool_sys_win.erl +++ b/lib/reltool/src/reltool_sys_win.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2009-2010. 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_sys_win). @@ -34,7 +34,7 @@ -include_lib("wx/include/wx.hrl"). -include("reltool.hrl"). --record(state, +-record(state, {parent_pid, server_pid, app_wins, @@ -61,7 +61,7 @@ -define(WIN_HEIGHT, 600). -define(CLOSE_ITEM, ?wxID_EXIT). %% Use OS specific version if available --define(ABOUT_ITEM, ?wxID_ABOUT). %% Use OS specific +-define(ABOUT_ITEM, ?wxID_ABOUT). %% Use OS specific -define(CONTENTS_ITEM, 300). -define(APP_GRAPH_ITEM, 301). -define(MOD_GRAPH_ITEM, 302). @@ -100,7 +100,11 @@ %% Client start_link(Opts) -> - proc_lib:start_link(?MODULE, init, [[{parent, self()} | Opts]], infinity, []). + proc_lib:start_link(?MODULE, + init, + [[{parent, self()} | Opts]], + infinity, + []). get_server(Pid) -> reltool_utils:call(Pid, get_server). @@ -146,9 +150,13 @@ do_init([{parent, Parent} | Options]) -> S3 = S2#state{sys = Sys2}, S5 = wx:batch(fun() -> Title = atom_to_list(?APPLICATION), - wxFrame:setTitle(S3#state.frame, Title), - %% wxFrame:setMinSize(Frame, {?WIN_WIDTH, ?WIN_HEIGHT}), - wxStatusBar:setStatusText(S3#state.status_bar, "Done."), + wxFrame:setTitle(S3#state.frame, + Title), + %% wxFrame:setMinSize(Frame, + %% {?WIN_WIDTH, ?WIN_HEIGHT}), + wxStatusBar:setStatusText( + S3#state.status_bar, + "Done."), S4 = redraw_apps(S3), redraw_libs(S4) end), @@ -182,7 +190,12 @@ loop(S) -> receive {system, From, Msg} -> Common = S#state.common, - sys:handle_system_msg(Msg, From, S#state.parent_pid, ?MODULE, Common#common.sys_debug, S); + sys:handle_system_msg(Msg, + From, + S#state.parent_pid, + ?MODULE, + Common#common.sys_debug, + S); #wx{obj = ObjRef, event = #wxClose{type = close_window}} = Msg -> if @@ -193,17 +206,22 @@ loop(S) -> FWs = S#state.fgraph_wins, case lists:keysearch(ObjRef, #fgraph_win.frame, FWs) of {value, FW} -> - reltool_fgraph_win:stop(FW#fgraph_win.pid, shutdown), + reltool_fgraph_win:stop(FW#fgraph_win.pid, + shutdown), wxFrame:destroy(ObjRef), - FWs2 = lists:keydelete(ObjRef, #fgraph_win.frame, FWs), + FWs2 = + 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]), + error_logger:format("~p~p got unexpected " + "message:\n\t~p\n", + [?MODULE, self(), Msg]), ?MODULE:loop(S) end end; - #wx{id = ?CLOSE_ITEM, event = #wxCommand{type = command_menu_selected}, userData = main_window} -> + #wx{id = ?CLOSE_ITEM, + event = #wxCommand{type = command_menu_selected}, + userData = main_window} -> wxFrame:destroy(S#state.frame), exit(shutdown); #wx{event = #wxSize{}} = Wx -> @@ -222,14 +240,18 @@ loop(S) -> ?MODULE:loop(S2); {call, ReplyTo, Ref, {open_app, AppName}} -> S2 = do_open_app(S, AppName), - {value, #app_win{pid = AppPid}} = lists:keysearch(AppName, #app_win.name, S2#state.app_wins), + {value, #app_win{pid = AppPid}} = + lists:keysearch(AppName, #app_win.name, S2#state.app_wins), reltool_utils:reply(ReplyTo, Ref, {ok, AppPid}), ?MODULE:loop(S2); {'EXIT', Pid, Reason} when Pid =:= S#state.parent_pid -> - [reltool_fgraph_win:stop(FW#fgraph_win.pid, Reason) || FW <- S#state.fgraph_wins], + [reltool_fgraph_win:stop(FW#fgraph_win.pid, Reason) || + FW <- S#state.fgraph_wins], exit(Reason); {'EXIT', _Pid, _Reason} = Exit -> - {FWs, AWs} = handle_child_exit(Exit, S#state.fgraph_wins, S#state.app_wins), + {FWs, AWs} = handle_child_exit(Exit, + S#state.fgraph_wins, + S#state.app_wins), ?MODULE:loop(S#state{fgraph_wins = FWs, app_wins = AWs}); Msg -> error_logger:format("~p~p got unexpected message:\n\t~p\n", @@ -261,7 +283,8 @@ msg_warning(Exit, Type) -> create_window(S) -> Title = lists:concat([?APPLICATION, " - starting up"]), - Frame = wxFrame:new(wx:null(), ?wxID_ANY, Title, [{size, {?WIN_WIDTH, ?WIN_HEIGHT}}]), + Frame = wxFrame:new(wx:null(), ?wxID_ANY, Title, + [{size, {?WIN_WIDTH, ?WIN_HEIGHT}}]), %%wxFrame:setSize(Frame, {?WIN_WIDTH, ?WIN_HEIGHT}), %% wxFrame:setMinSize(Frame, {?WIN_WIDTH, ?WIN_HEIGHT}), Bar = wxFrame:createStatusBar(Frame,[]), @@ -306,21 +329,29 @@ create_menubar(Frame) -> File = wxMenu:new([]), Help = wxMenu:new([]), wxMenuBar:append(MenuBar, File, "File" ), - wxMenu:append(File, ?APP_GRAPH_ITEM, "Display application dependency graph" ), - wxMenu:append(File, ?MOD_GRAPH_ITEM, "Display module dependency graph" ), + wxMenu:append(File, ?APP_GRAPH_ITEM, + "Display application dependency graph" ), + wxMenu:append(File, ?MOD_GRAPH_ITEM, + "Display module dependency graph" ), wxMenu:appendSeparator(File), wxMenu:append(File, ?RESET_CONFIG_ITEM, "Reset configuration to default" ), wxMenu:append(File, ?UNDO_CONFIG_ITEM, "Undo configuration (toggle)" ), wxMenu:append(File, ?LOAD_CONFIG_ITEM, "Load configuration" ), Save = wxMenu:new(), - wxMenu:append(Save, ?SAVE_CONFIG_NODEF_NODER_ITEM, "Save explicit configuration (neither defaults nor derivates)"), - wxMenu:append(Save, ?SAVE_CONFIG_DEF_NODER_ITEM , "Save configuration defaults (defaults only)"), - wxMenu:append(Save, ?SAVE_CONFIG_NODEF_DER_ITEM, "Save configuration derivates (derivates only))"), - wxMenu:append(Save, ?SAVE_CONFIG_DEF_DER_ITEM, "Save extended configuration (both defaults and derivates)"), + wxMenu:append(Save, ?SAVE_CONFIG_NODEF_NODER_ITEM, + "Save explicit configuration " + "(neither defaults nor derivates)"), + wxMenu:append(Save, ?SAVE_CONFIG_DEF_NODER_ITEM, + "Save configuration defaults (defaults only)"), + wxMenu:append(Save, ?SAVE_CONFIG_NODEF_DER_ITEM, + "Save configuration derivates (derivates only))"), + wxMenu:append(Save, ?SAVE_CONFIG_DEF_DER_ITEM, + "Save extended configuration (both defaults and derivates)"), wxMenu:append(File, ?wxID_ANY, "Save configuration", Save), wxMenu:appendSeparator(File), - wxMenu:append(File, ?GEN_REL_FILES_ITEM, "Generate rel, script and boot files" ), + wxMenu:append(File, ?GEN_REL_FILES_ITEM, + "Generate rel, script and boot files" ), wxMenu:append(File, ?GEN_TARGET_ITEM, "Generate target system" ), wxMenu:appendSeparator(File), wxMenu:append(File, ?CLOSE_ITEM, "Close" ), @@ -375,12 +406,13 @@ create_app_list_ctrl(Panel, OuterSz, Title, Tick, Cross) -> %% ?wxLC_SINGLE_SEL bor ?wxVSCROLL}, {size, {Width, Height}}]), - ToolTip = "Select application(s) or open separate application window with a double click.", + ToolTip = "Select application(s) or open separate " + "application window with a double click.", wxListCtrl:setToolTip(ListCtrl, ToolTip), %% Prep images reltool_utils:assign_image_list(ListCtrl), - + %% Prep column label ListItem = wxListItem:new(), wxListItem:setAlign(ListItem, ?wxLIST_FORMAT_LEFT), @@ -395,7 +427,7 @@ create_app_list_ctrl(Panel, OuterSz, Title, Tick, Cross) -> InnerSz = wxBoxSizer:new(?wxVERTICAL), - wxSizer:add(InnerSz, + wxSizer:add(InnerSz, ListCtrl, [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}, @@ -408,9 +440,10 @@ create_app_list_ctrl(Panel, OuterSz, Title, Tick, Cross) -> [{flag, ?wxEXPAND}, {proportion, 1}]), %% Subscribe on events - wxEvtHandler:connect(ListCtrl, size, [{skip, true}, {userData, app_list_ctrl}]), + wxEvtHandler:connect(ListCtrl, size, + [{skip, true}, {userData, app_list_ctrl}]), wxEvtHandler:connect(ListCtrl, command_list_item_activated), - wxWindow:connect(ListCtrl, enter_window), + wxWindow:connect(ListCtrl, enter_window), ListCtrl. @@ -423,7 +456,7 @@ create_button(Panel, Sizer, ListCtrl, Title, BitMapName, Action) -> wxBitmapButton:setToolTip(Button, ToolTip), Options = [{userData, {app_button, Action, ListCtrl}}], wxEvtHandler:connect(Button, command_button_clicked, Options), - wxSizer:add(Sizer, + wxSizer:add(Sizer, Button, [{border, 2}, {flag, ?wxALL}, @@ -439,7 +472,7 @@ action_to_tool_tip(Label, Action) -> "Remove selected application(s)from whitelist."; blacklist_add when Label =:= ?blacklist -> "Remove selected application(s) from blacklist."; - blacklist_add -> + blacklist_add -> "Add selected application(s) to blacklist."; blacklist_del -> "Remove selected application(s) from blacklist." @@ -448,8 +481,9 @@ action_to_tool_tip(Label, Action) -> create_lib_page(#state{book = Book} = S) -> Panel = wxPanel:new(Book, []), Sizer = wxBoxSizer:new(?wxHORIZONTAL), - - Tree = wxTreeCtrl:new(Panel, [{style , ?wxTR_HAS_BUTTONS bor ?wxTR_HIDE_ROOT}]), + + Tree = wxTreeCtrl:new(Panel, + [{style , ?wxTR_HAS_BUTTONS bor ?wxTR_HIDE_ROOT}]), ToolTip = "Edit application sources.", wxBitmapButton:setToolTip(Tree, ToolTip), @@ -478,7 +512,9 @@ redraw_libs(#state{lib_tree = Tree, sys = Sys} = S) -> [append_lib(Tree, LibItem, Dir) || Dir <- Sys#sys.lib_dirs], EscriptItem = append_item(Tree, Top, "Escript files", undefined), - EscriptData = #escript_data{file = undefined, tree = Tree, item = EscriptItem}, + EscriptData = #escript_data{file = undefined, + tree = Tree, + item = EscriptItem}, wxTreeCtrl:setItemData(Tree,EscriptItem, EscriptData), [append_escript(Tree, EscriptItem, File) || File <- Sys#sys.escripts], wxTreeCtrl:expand(Tree, LibItem), @@ -529,9 +565,9 @@ create_config_page(#state{sys = Sys, book = Book} = S) -> Panel = wxPanel:new(Book, []), Sizer = wxBoxSizer:new(?wxHORIZONTAL), AppConds = reltool_utils:incl_conds(), - AppBox = wxRadioBox:new(Panel, + AppBox = wxRadioBox:new(Panel, ?wxID_ANY, - "Application inclusion policy", + "Application inclusion policy", ?wxDefaultPosition, ?wxDefaultSize, AppConds, @@ -543,9 +579,9 @@ create_config_page(#state{sys = Sys, book = Book} = S) -> wxEvtHandler:connect(AppBox, command_radiobox_selected, [{userData, config_incl_cond}]), ModConds = reltool_utils:mod_conds(), - ModBox = wxRadioBox:new(Panel, + ModBox = wxRadioBox:new(Panel, ?wxID_ANY, - "Module inclusion policy", + "Module inclusion policy", ?wxDefaultPosition, ?wxDefaultSize, ModConds, @@ -582,19 +618,19 @@ create_main_release_page(#state{book = Book} = S) -> wxButton:setToolTip(Create, "Create a new release."), wxButton:connect(Create, command_button_clicked, [{userData, create_rel}]), wxSizer:add(ButtonSizer, Create), - + Delete = wxButton:new(Panel, ?wxID_ANY, [{label, "Delete"}]), wxButton:setToolTip(Delete, "Delete a release."), wxButton:connect(Delete, command_button_clicked, [{userData, delete_rel}]), wxSizer:add(ButtonSizer, Delete), - + View = wxButton:new(Panel, ?wxID_ANY, [{label, "View script"}]), wxButton:setToolTip(View, "View generated script file."), wxButton:connect(View, command_button_clicked, [{userData, view_script}]), wxSizer:add(ButtonSizer, View), [add_release_page(RelBook, Rel) || Rel <- (S#state.sys)#sys.rels], - + wxSizer:add(Sizer, RelBook, [{flag, ?wxEXPAND}, {proportion, 1}]), wxSizer:add(Sizer, ButtonSizer, [{flag, ?wxEXPAND}]), wxPanel:setSizer(Panel, Sizer), @@ -604,16 +640,18 @@ create_main_release_page(#state{book = Book} = S) -> add_release_page(Book, #rel{name = RelName, rel_apps = RelApps}) -> Panel = wxPanel:new(Book, []), Sizer = wxBoxSizer:new(?wxHORIZONTAL), - RelBox = wxRadioBox:new(Panel, + RelBox = wxRadioBox:new(Panel, ?wxID_ANY, - "Applications included in the release " ++ RelName, + "Applications included in the release " ++ RelName, ?wxDefaultPosition, ?wxDefaultSize, [atom_to_list(RA#rel_app.name) || RA <- RelApps], []), %% wxRadioBox:setSelection(RelBox, 2), % mandatory - wxEvtHandler:connect(RelBox, command_radiobox_selected, [{userData, {config_rel_cond, RelName}}]), - RelToolTip = "Choose which applications that shall be included in the release resource file.", + wxEvtHandler:connect(RelBox, command_radiobox_selected, + [{userData, {config_rel_cond, RelName}}]), + RelToolTip = "Choose which applications that shall " + "be included in the release resource file.", wxBitmapButton:setToolTip(RelBox, RelToolTip), wxSizer:add(Sizer, @@ -629,11 +667,14 @@ do_open_app(S, AppBase) when is_list(AppBase) -> do_open_app(S, AppName); do_open_app(S, '') -> S; -do_open_app(#state{server_pid = ServerPid, common = C, app_wins = AppWins} = S, AppName) when is_atom(AppName) -> +do_open_app(#state{server_pid = ServerPid, common = C, app_wins = AppWins} = S, + AppName) + when is_atom(AppName) -> case lists:keysearch(AppName, #app_win.name, AppWins) of false -> WxEnv = wx:get_env(), - {ok, Pid} = reltool_app_win:start_link(WxEnv, ServerPid, C, AppName), + {ok, Pid} = + reltool_app_win:start_link(WxEnv, ServerPid, C, AppName), AW = #app_win{name = AppName, pid = Pid}, S#state{app_wins = [AW | AppWins]}; {value, AW} -> @@ -651,7 +692,10 @@ root_popup(S, Root, Tree, Item) -> wxEvtHandler:connect(PopupMenu, menu_close), wxWindow:popupMenu(S#state.frame, PopupMenu), - Popup = #root_popup{dir = Root, choices = Choices, tree = Tree, item = Item}, + Popup = #root_popup{dir = Root, + choices = Choices, + tree = Tree, + item = Item}, S#state{popup_menu = Popup}. lib_popup(S, Lib, Tree, Item) -> @@ -693,7 +737,10 @@ escript_popup(S, File, Tree, Item) -> wxEvtHandler:connect(PopupMenu, menu_close), wxWindow:popupMenu(S#state.frame, PopupMenu), - Popup = #escript_popup{file = File, choices = Choices, tree = Tree, item = Item}, + Popup = #escript_popup{file = File, + choices = Choices, + tree = Tree, + item = Item}, S#state{popup_menu = Popup}. @@ -705,29 +752,40 @@ handle_event(S, #wx{id = Id, obj= ObjRef, userData = UserData, event = Event} = #wxSize{type = size, size = {W, _H}} when UserData =:= app_list_ctrl -> wxListCtrl:setColumnWidth(ObjRef, ?APPS_APP_COL, W), S; - #wxCommand{type = command_menu_selected} when Id =:= ?APP_GRAPH_ITEM -> + #wxCommand{type = command_menu_selected} + when Id =:= ?APP_GRAPH_ITEM -> update_app_graph(S); - #wxCommand{type = command_menu_selected} when Id =:= ?MOD_GRAPH_ITEM -> + #wxCommand{type = command_menu_selected} + when Id =:= ?MOD_GRAPH_ITEM -> update_mod_graph(S); - #wxCommand{type = command_menu_selected} when Id =:= ?RESET_CONFIG_ITEM -> + #wxCommand{type = command_menu_selected} + when Id =:= ?RESET_CONFIG_ITEM -> reset_config(S); - #wxCommand{type = command_menu_selected} when Id =:= ?UNDO_CONFIG_ITEM -> + #wxCommand{type = command_menu_selected} + when Id =:= ?UNDO_CONFIG_ITEM -> undo_config(S); - #wxCommand{type = command_menu_selected} when Id =:= ?LOAD_CONFIG_ITEM -> + #wxCommand{type = command_menu_selected} + when Id =:= ?LOAD_CONFIG_ITEM -> load_config(S); - #wxCommand{type = command_menu_selected} when Id =:= ?SAVE_CONFIG_NODEF_NODER_ITEM -> + #wxCommand{type = command_menu_selected} + when Id =:= ?SAVE_CONFIG_NODEF_NODER_ITEM -> save_config(S, false, false); - #wxCommand{type = command_menu_selected} when Id =:= ?SAVE_CONFIG_NODEF_DER_ITEM -> + #wxCommand{type = command_menu_selected} + when Id =:= ?SAVE_CONFIG_NODEF_DER_ITEM -> save_config(S, false, true); #wxCommand{type = command_menu_selected} when Id =:= ?SAVE_CONFIG_DEF_NODER_ITEM -> save_config(S, true, false); - #wxCommand{type = command_menu_selected} when Id =:= ?SAVE_CONFIG_DEF_DER_ITEM -> + #wxCommand{type = command_menu_selected} + when Id =:= ?SAVE_CONFIG_DEF_DER_ITEM -> save_config(S, true, true); - #wxCommand{type = command_menu_selected} when Id =:= ?GEN_REL_FILES_ITEM -> + #wxCommand{type = command_menu_selected} + when Id =:= ?GEN_REL_FILES_ITEM -> gen_rel_files(S); - #wxCommand{type = command_menu_selected} when Id =:= ?GEN_TARGET_ITEM -> + #wxCommand{type = command_menu_selected} + when Id =:= ?GEN_TARGET_ITEM -> gen_target(S); - #wxCommand{type = command_menu_selected} when UserData =:= main_window, Id =:= ?CONTENTS_ITEM -> + #wxCommand{type = command_menu_selected} + when UserData =:= main_window, Id =:= ?CONTENTS_ITEM -> {file, BeamFile} = code:is_loaded(?MODULE), EbinDir = filename:dirname(BeamFile), AppDir = filename:dirname(EbinDir), @@ -735,16 +793,17 @@ handle_event(S, #wx{id = Id, obj= ObjRef, userData = UserData, event = Event} = Url = "file://" ++ filename:absname(HelpFile), wx_misc:launchDefaultBrowser(Url), S; - #wxCommand{type = command_menu_selected} when UserData =:= main_window, Id =:= ?ABOUT_ITEM -> - AboutStr = "Reltool is a release management tool. It analyses a given" - " Erlang/OTP installation and determines various dependencies" - " between applications. The graphical frontend depicts the" - " dependencies and enables interactive customization of a" - " target system. The backend provides a batch interface" - " for generation of customized target systems.", - MD = wxMessageDialog:new(S#state.frame, + #wxCommand{type = command_menu_selected} + when UserData =:= main_window, Id =:= ?ABOUT_ITEM -> + AboutStr = "Reltool is a release management tool. It analyses a " + " given Erlang/OTP installation and determines various " + " dependencies between applications. The graphical frontend " + " depicts the dependencies and enables interactive " + " customization of a target system. The backend provides a " + " batch interface for generation of customized target systems.", + MD = wxMessageDialog:new(S#state.frame, AboutStr, - [{style, ?wxOK bor ?wxICON_INFORMATION}, + [{style, ?wxOK bor ?wxICON_INFORMATION}, {caption, "About Reltool"}]), wxMessageDialog:showModal(MD), wxMessageDialog:destroy(MD), @@ -758,7 +817,8 @@ handle_event(S, #wx{id = Id, obj= ObjRef, userData = UserData, event = Event} = wxWindow:setFocus(ObjRef), S; _ -> - case wxNotebook:getPageText(S#state.book, wxNotebook:getSelection(S#state.book)) of + case wxNotebook:getPageText(S#state.book, + wxNotebook:getSelection(S#state.book)) of ?APP_PAGE -> handle_app_event(S, Event, ObjRef, UserData); ?LIB_PAGE -> handle_source_event(S, Event, ObjRef, UserData); ?SYS_PAGE -> handle_system_event(S, Event, ObjRef, UserData); @@ -768,13 +828,17 @@ handle_event(S, #wx{id = Id, obj= ObjRef, userData = UserData, event = Event} = handle_popup_event(S, _Type, 0, _ObjRef, _UserData, _Str) -> S#state{popup_menu = undefined}; -handle_popup_event(#state{popup_menu = #root_popup{dir = OldDir, choices = Choices}, +handle_popup_event(#state{popup_menu = #root_popup{dir = OldDir, + choices = Choices}, sys = Sys} = S, _Type, Pos, _ObjRef, _UserData, _Str) -> case lists:nth(Pos, Choices) of edit -> Style = ?wxFD_OPEN bor ?wxFD_FILE_MUST_EXIST, - case select_dir(S#state.frame, "Change root directory", OldDir, Style) of + case select_dir(S#state.frame, + "Change root directory", + OldDir, + Style) of {ok, NewDir} when NewDir =:= OldDir -> %% Same dir.Ignore. S#state{popup_menu = undefined}; @@ -785,7 +849,8 @@ handle_popup_event(#state{popup_menu = #root_popup{dir = OldDir, choices = Choic S#state{popup_menu = undefined} end end; -handle_popup_event(#state{popup_menu = #lib_popup{dir = OldDir, choices = Choices}, +handle_popup_event(#state{popup_menu = #lib_popup{dir = OldDir, + choices = Choices}, sys = Sys} = S, _Type, Pos, _ObjRef, _UserData, _Str) -> case lists:nth(Pos, Choices) of @@ -801,14 +866,18 @@ handle_popup_event(#state{popup_menu = #lib_popup{dir = OldDir, choices = Choice false -> LibDirs = Sys#sys.lib_dirs ++ [NewDir], Sys2 = Sys#sys{lib_dirs = LibDirs}, - do_set_sys(S#state{popup_menu = undefined, sys = Sys2}) + do_set_sys(S#state{popup_menu = undefined, + sys = Sys2}) end; cancel -> S#state{popup_menu = undefined} end; edit -> Style = ?wxFD_OPEN bor ?wxFD_FILE_MUST_EXIST, - case select_dir(S#state.frame, "Change library directory", OldDir, Style) of + case select_dir(S#state.frame, + "Change library directory", + OldDir, + Style) of {ok, NewDir} -> case lists:member(NewDir, Sys#sys.lib_dirs) of true -> @@ -820,7 +889,8 @@ handle_popup_event(#state{popup_menu = #lib_popup{dir = OldDir, choices = Choice lists:splitwith(Pred, Sys#sys.lib_dirs), LibDirs2 = Before ++ [NewDir | After], Sys2 = Sys#sys{lib_dirs = LibDirs2}, - do_set_sys(S#state{popup_menu = undefined, sys = Sys2}) + do_set_sys(S#state{popup_menu = undefined, + sys = Sys2}) end; cancel -> S#state{popup_menu = undefined} @@ -828,14 +898,15 @@ handle_popup_event(#state{popup_menu = #lib_popup{dir = OldDir, choices = Choice delete -> LibDirs = Sys#sys.lib_dirs -- [OldDir], Sys2 = Sys#sys{lib_dirs = LibDirs}, - do_set_sys(S#state{popup_menu = undefined, sys = Sys2}) + do_set_sys(S#state{popup_menu = undefined, sys = Sys2}) end; -handle_popup_event(#state{popup_menu = #escript_popup{file = OldFile, choices = Choices}, +handle_popup_event(#state{popup_menu = #escript_popup{file = OldFile, + choices = Choices}, sys = Sys} = S, _Type, Pos, _ObjRef, _UserData, _Str) -> case lists:nth(Pos, Choices) of add -> - OldFile2 = + OldFile2 = case OldFile of undefined -> {ok, Cwd} = file:get_cwd(), @@ -844,7 +915,10 @@ handle_popup_event(#state{popup_menu = #escript_popup{file = OldFile, choices = OldFile end, Style = ?wxFD_OPEN bor ?wxFD_FILE_MUST_EXIST, - case select_file(S#state.frame, "Select an escript file to add", OldFile2, Style) of + case select_file(S#state.frame, + "Select an escript file to add", + OldFile2, + Style) of {ok, NewFile} -> case lists:member(NewFile, Sys#sys.escripts) of true -> @@ -860,7 +934,10 @@ handle_popup_event(#state{popup_menu = #escript_popup{file = OldFile, choices = end; edit -> Style = ?wxFD_OPEN bor ?wxFD_FILE_MUST_EXIST, - case select_file(S#state.frame, "Change escript file name", OldFile, Style) of + case select_file(S#state.frame, + "Change escript file name", + OldFile, + Style) of {ok, NewFile} -> case lists:member(NewFile, Sys#sys.escripts) of true -> @@ -868,10 +945,12 @@ handle_popup_event(#state{popup_menu = #escript_popup{file = OldFile, choices = S#state{popup_menu = undefined}; false -> Pred = fun(E) -> E =/= OldFile end, - {Before, [_| After]} = lists:splitwith(Pred, Sys#sys.escripts), + {Before, [_| After]} = + lists:splitwith(Pred, Sys#sys.escripts), Escripts2 = Before ++ [NewFile | After], Sys2 = Sys#sys{escripts = Escripts2}, - do_set_sys(S#state{popup_menu = undefined, sys = Sys2}) + do_set_sys(S#state{popup_menu = undefined, + sys = Sys2}) end; cancel -> S#state{popup_menu = undefined} @@ -879,25 +958,28 @@ handle_popup_event(#state{popup_menu = #escript_popup{file = OldFile, choices = delete -> Escripts = Sys#sys.escripts -- [OldFile], Sys2 = Sys#sys{escripts = Escripts}, - do_set_sys(S#state{popup_menu = undefined, sys = Sys2}) + do_set_sys(S#state{popup_menu = undefined, sys = Sys2}) end. handle_system_event(#state{sys = Sys} = S, - #wxCommand{type = command_radiobox_selected, cmdString = Choice}, + #wxCommand{type = command_radiobox_selected, + cmdString = Choice}, _ObjRef, config_mod_cond) -> ModCond = reltool_utils:list_to_mod_cond(Choice), Sys2 = Sys#sys{mod_cond = ModCond}, do_set_sys(S#state{sys = Sys2}); handle_system_event(#state{sys = Sys} = S, - #wxCommand{type = command_radiobox_selected, cmdString = Choice}, + #wxCommand{type = command_radiobox_selected, + cmdString = Choice}, _ObjRef, config_incl_cond) -> AppCond = reltool_utils:list_to_incl_cond(Choice), Sys2 = Sys#sys{incl_cond = AppCond}, do_set_sys(S#state{sys = Sys2}); handle_system_event(S, Event, ObjRef, UserData) -> - error_logger:format("~p~p got unexpected wx sys event to ~p with user data: ~p\n\t ~p\n", + error_logger:format("~p~p got unexpected wx sys event to ~p " + "with user data: ~p\n\t ~p\n", [?MODULE, self(), ObjRef, UserData, Event]), S. @@ -905,7 +987,11 @@ handle_release_event(S, _Event, _ObjRef, UserData) -> io:format("Release data: ~p\n", [UserData]), S. -handle_source_event(S, #wxTree{type = command_tree_item_activated, item = Item}, ObjRef, _UserData) -> +handle_source_event(S, + #wxTree{type = command_tree_item_activated, + item = Item}, + ObjRef, + _UserData) -> case wxTreeCtrl:getItemData(ObjRef, Item) of #root_data{dir = _Dir} -> %% io:format("Root dialog: ~p\n", [Dir]), @@ -921,7 +1007,11 @@ handle_source_event(S, #wxTree{type = command_tree_item_activated, item = Item}, undefined -> S end; -handle_source_event(S, #wxTree{type = command_tree_item_right_click, item = Item}, Tree, _UserData) -> +handle_source_event(S, + #wxTree{type = command_tree_item_right_click, + item = Item}, + Tree, + _UserData) -> case wxTreeCtrl:getItemData(Tree, Item) of #root_data{dir = Dir} -> wx:batch(fun() -> root_popup(S, Dir, Tree, Item) end); @@ -936,18 +1026,28 @@ handle_source_event(S, #wxTree{type = command_tree_item_right_click, item = Item S end. -handle_app_event(S, #wxList{type = command_list_item_activated, itemIndex = Pos}, ListCtrl, _UserData) -> +handle_app_event(S, + #wxList{type = command_list_item_activated, + itemIndex = Pos}, + ListCtrl, + _UserData) -> AppName = wxListCtrl:getItemText(ListCtrl, Pos), do_open_app(S, AppName); -handle_app_event(S, #wxCommand{type = command_button_clicked}, _ObjRef, {app_button, Action, ListCtrl}) -> +handle_app_event(S, + #wxCommand{type = command_button_clicked}, + _ObjRef, + {app_button, Action, ListCtrl}) -> Items = reltool_utils:get_items(ListCtrl), handle_app_button(S, Items, Action); handle_app_event(S, Event, ObjRef, UserData) -> - error_logger:format("~p~p got unexpected wx app event to ~p with user data: ~p\n\t ~p\n", + error_logger:format("~p~p got unexpected wx app event to " + "~p with user data: ~p\n\t ~p\n", [?MODULE, self(), ObjRef, UserData, Event]), S. -handle_app_button(#state{server_pid = ServerPid, app_wins = AppWins} = S, Items, Action) -> +handle_app_button(#state{server_pid = ServerPid, app_wins = AppWins} = S, + Items, + Action) -> NewApps = [move_app(S, Item, Action) || Item <- Items], case reltool_server:set_apps(ServerPid, NewApps) of {ok, []} -> @@ -967,9 +1067,9 @@ do_set_sys(#state{sys = Sys, server_pid = ServerPid, status_bar = Bar} = S) -> check_and_refresh(S, Status). move_app(S, {_ItemNo, AppBase}, Action) -> - {AppName, _Vsn} = reltool_utils:split_app_name(AppBase), + {AppName, _Vsn} = reltool_utils:split_app_name(AppBase), {ok, OldApp} = reltool_server:get_app(S#state.server_pid, AppName), - AppCond = + AppCond = case Action of whitelist_add -> case OldApp#app.incl_cond of @@ -979,12 +1079,13 @@ move_app(S, {_ItemNo, AppBase}, Action) -> end; whitelist_del -> undefined; - blacklist_add -> + blacklist_add -> exclude; blacklist_del -> undefined; _ -> - error_logger:format("~p~p got unexpected app button event: ~p ~p\n", + error_logger:format("~p~p got unexpected app " + "button event: ~p ~p\n", [?MODULE, self(), Action, AppBase]), OldApp#app.incl_cond end, @@ -1047,14 +1148,21 @@ do_redraw_apps(ListCtrl, Apps, OkImage, ErrImage) -> ImageApps = lists:map(AddImage, Apps), Show = fun({ImageId, Text, App}, {Row, ModCount, Items}) -> - wxListCtrl:insertItem(ListCtrl, Row, ""), - if (Row rem 2) =:= 0 -> - wxListCtrl:setItemBackgroundColour(ListCtrl, Row, {240,240,255}); + wxListCtrl:insertItem(ListCtrl, Row, ""), + if (Row rem 2) =:= 0 -> + wxListCtrl:setItemBackgroundColour(ListCtrl, + Row, + {240,240,255}); true -> ignore end, - wxListCtrl:setItem(ListCtrl, Row, ?APPS_APP_COL, Text, [{imageId, ImageId}]), - N = length([M || M <- App#app.mods, M#mod.is_included =:= true]), + wxListCtrl:setItem(ListCtrl, + Row, + ?APPS_APP_COL, + Text, + [{imageId, ImageId}]), + N = length([M || M <- App#app.mods, + M#mod.is_included =:= true]), {Row + 1, ModCount + N, [{Row, Text} | Items]} end, {_, N, NewItems} = wx:foldl(Show, {0, 0, []}, lists:sort(ImageApps)), @@ -1068,8 +1176,10 @@ update_app_graph(S) -> WhiteNames = [A#app.name || A <- WhiteApps], DerivedNames = [A#app.name || A <- DerivedApps], Nodes = WhiteNames ++ DerivedNames, - %% WhiteUses = [N || A <- WhiteApps, N <- A#app.uses_apps, lists:member(N, Nodes)], - %% DerivedUses = [N || A <- DerivedApps, N <- A#app.uses_apps, lists:member(N, Nodes)], + %% WhiteUses = [N || A <- WhiteApps, + %% N <- A#app.uses_apps, lists:member(N, Nodes)], + %% DerivedUses = [N || A <- DerivedApps, + %% N <- A#app.uses_apps, lists:member(N, Nodes)], WhiteLinks = [[A#app.name, U] || A <- WhiteApps, U <- A#app.uses_apps, @@ -1078,7 +1188,7 @@ update_app_graph(S) -> DerivedLinks = [[A#app.name, U] || A <- DerivedApps, U <- A#app.uses_apps, U =/= A#app.name, - lists:member(U, Nodes)], + lists:member(U, Nodes)], Links = lists:usort(WhiteLinks ++ DerivedLinks), %% io:format("Links: ~p\n", [Links]), Title = lists:concat([?APPLICATION, " - application graph"]), @@ -1087,8 +1197,12 @@ update_app_graph(S) -> update_mod_graph(S) -> {ok, WhiteApps} = reltool_server:get_apps(S#state.server_pid, whitelist), {ok, DerivedApps} = reltool_server:get_apps(S#state.server_pid, derived), - WhiteMods = lists:usort([M || A <- WhiteApps, M <- A#app.mods, M#mod.is_included =:= true]), - DerivedMods = lists:usort([M || A <- DerivedApps, M <- A#app.mods, M#mod.is_included =:= true]), + WhiteMods = lists:usort([M || A <- WhiteApps, + M <- A#app.mods, + M#mod.is_included =:= true]), + DerivedMods = lists:usort([M || A <- DerivedApps, + M <- A#app.mods, + M#mod.is_included =:= true]), WhiteNames = [M#mod.name || M <- WhiteMods], DerivedNames = [M#mod.name || M <- DerivedMods], @@ -1113,7 +1227,7 @@ create_fgraph_window(S, Title, Nodes, Links) -> Panel = wxPanel:new(Frame, []), Options = [{size, {lists:max([100, ?WIN_WIDTH - 100]), ?WIN_HEIGHT}}], {Server, Fgraph} = reltool_fgraph_win:new(Panel, Options), - Choose = fun(?MISSING_APP) -> alternate; + Choose = fun(?MISSING_APP_NAME) -> alternate; (_) -> default end, [reltool_fgraph_win:add_node(Server, N, Choose(N)) || N <- Nodes], @@ -1141,7 +1255,10 @@ undo_config(#state{status_bar = Bar} = S) -> load_config(#state{status_bar = Bar, config_file = OldFile} = S) -> Style = ?wxFD_OPEN bor ?wxFD_FILE_MUST_EXIST, - case select_file(S#state.frame, "Select a file to load the configuration from", OldFile, Style) of + case select_file(S#state.frame, + "Select a file to load the configuration from", + OldFile, + Style) of {ok, NewFile} -> wxStatusBar:setStatusText(Bar, "Processing libraries..."), Status = reltool_server:load_config(S#state.server_pid, NewFile), @@ -1151,18 +1268,27 @@ load_config(#state{status_bar = Bar, config_file = OldFile} = S) -> end. save_config(#state{config_file = OldFile} = S, InclDefaults, InclDerivates) -> - Style = ?wxFD_SAVE bor ?wxFD_OVERWRITE_PROMPT, - case select_file(S#state.frame, "Select a file to save the configuration to", OldFile, Style) of + Style = ?wxFD_SAVE bor ?wxFD_OVERWRITE_PROMPT, + case select_file(S#state.frame, + "Select a file to save the configuration to", + OldFile, + Style) of {ok, NewFile} -> - Status = reltool_server:save_config(S#state.server_pid, NewFile, InclDefaults, InclDerivates), + Status = reltool_server:save_config(S#state.server_pid, + NewFile, + InclDefaults, + InclDerivates), check_and_refresh(S#state{config_file = NewFile}, Status); cancel -> S end. gen_rel_files(#state{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 + 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} -> Status = reltool_server:gen_rel_files(S#state.server_pid, NewDir), check_and_refresh(S, Status); @@ -1171,8 +1297,11 @@ gen_rel_files(#state{target_dir = OldDir} = S) -> end. gen_target(#state{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 + 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} -> Status = reltool_server:gen_target(S#state.server_pid, NewDir), check_and_refresh(S#state{target_dir = NewDir}, Status); @@ -1186,7 +1315,7 @@ select_file(Frame, Message, DefaultFile, Style) -> {defaultDir, filename:dirname(DefaultFile)}, {defaultFile, filename:basename(DefaultFile)}, {style, Style}]), - Choice = + Choice = case wxMessageDialog:showModal(Dialog) of ?wxID_CANCEL -> cancel; ?wxID_OK -> {ok, wxFileDialog:getPath(Dialog)} @@ -1199,7 +1328,7 @@ select_dir(Frame, Message, DefaultDir, Style) -> [{title, Message}, {defaultPath, DefaultDir}, {style, Style}]), - Choice = + Choice = case wxMessageDialog:showModal(Dialog) of ?wxID_CANCEL -> cancel; ?wxID_OK -> {ok, wxDirDialog:getPath(Dialog)} @@ -1229,28 +1358,34 @@ refresh(S) -> S2 = S#state{sys = Sys}, S3 = redraw_libs(S2), redraw_apps(S3). - + question_dialog(Question, Details) -> %% Parent = S#state.frame, Parent = wx:typeCast(wx:null(), wxWindow), %% [{style, ?wxYES_NO bor ?wxICON_ERROR bor ?wx}]), DialogStyle = ?wxRESIZE_BORDER bor ?wxCAPTION bor ?wxSYSTEM_MENU bor ?wxMINIMIZE_BOX bor ?wxMAXIMIZE_BOX bor ?wxCLOSE_BOX, - Dialog = wxDialog:new(Parent, ?wxID_ANY, "Undo dialog", [{style, DialogStyle}]), + Dialog = wxDialog:new(Parent, ?wxID_ANY, "Undo dialog", + [{style, DialogStyle}]), Color = wxWindow:getBackgroundColour(Dialog), TextStyle = ?wxTE_READONLY bor ?wxTE_MULTILINE bor ?wxHSCROLL, - Text1 = wxTextCtrl:new(Dialog, ?wxID_ANY, [{style, ?wxTE_READONLY bor ?wxBORDER_NONE}]), + Text1 = wxTextCtrl:new(Dialog, ?wxID_ANY, + [{style, ?wxTE_READONLY bor ?wxBORDER_NONE}]), wxWindow:setBackgroundColour(Text1, Color), wxTextCtrl:appendText(Text1, Question), - Text2 = wxTextCtrl:new(Dialog, ?wxID_ANY, [{size, {600, 400}}, {style, TextStyle}]), + Text2 = wxTextCtrl:new(Dialog, ?wxID_ANY, + [{size, {600, 400}}, {style, TextStyle}]), wxWindow:setBackgroundColour(Text2, Color), wxTextCtrl:appendText(Text2, Details), %% wxDialog:setAffirmativeId(Dialog, ?wxID_YES), %% wxDialog:setEscapeId(Dialog, ?wxID_NO), Sizer = wxBoxSizer:new(?wxVERTICAL), wxSizer:add(Sizer, Text1, [{border, 2}, {flag, ?wxEXPAND}]), - wxSizer:add(Sizer, Text2, [{border, 2}, {flag, ?wxEXPAND}, {proportion, 1}]), - ButtSizer = wxDialog:createStdDialogButtonSizer(Dialog, ?wxOK bor ?wxCANCEL), + wxSizer:add(Sizer, Text2, [{border, 2}, + {flag, ?wxEXPAND}, + {proportion, 1}]), + ButtSizer = wxDialog:createStdDialogButtonSizer(Dialog, + ?wxOK bor ?wxCANCEL), wxSizer:add(Sizer, ButtSizer, [{border, 2}, {flag, ?wxEXPAND}]), wxPanel:setSizer(Dialog, Sizer), wxSizer:fit(Sizer, Dialog), diff --git a/lib/reltool/src/reltool_target.erl b/lib/reltool/src/reltool_target.erl index 6d85a98d9f..0fcf89a360 100644 --- a/lib/reltool/src/reltool_target.erl +++ b/lib/reltool/src/reltool_target.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2010. All Rights Reserved. +%% Copyright Ericsson AB 2009-2011. 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 @@ -22,7 +22,7 @@ -export([ gen_config/2, gen_app/1, - gen_rel/2, + gen_rel/2, gen_rel_files/2, gen_boot/1, gen_script/4, @@ -31,7 +31,7 @@ gen_target/2, install/2 ]). --compile(export_all). + -include("reltool.hrl"). -include_lib("kernel/include/file.hrl"). @@ -55,147 +55,186 @@ kernel_processes(KernelApp) -> [ {kernelProcess, heart, {heart, start, []}}, {kernelProcess, error_logger , {error_logger, start_link, []}}, - {kernelProcess, application_controller, {application_controller, start, [KernelApp]}} + {kernelProcess, + application_controller, + {application_controller, start, [KernelApp]}} ]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Generate the contents of a config file %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -gen_config(#sys{root_dir = RootDir, - lib_dirs = LibDirs, - mod_cond = ModCond, - incl_cond = AppCond, - apps = Apps, - boot_rel = BootRel, - rels = Rels, - emu_name = EmuName, - profile = Profile, - incl_sys_filters = InclSysFiles, - excl_sys_filters = ExclSysFiles, - incl_app_filters = InclAppFiles, - excl_app_filters = ExclAppFiles, - incl_archive_filters = InclArchiveDirs, - excl_archive_filters = ExclArchiveDirs, - archive_opts = ArchiveOpts, - relocatable = Relocatable, - app_type = AppType, - app_file = AppFile, - debug_info = DebugInfo}, - InclDefaults) -> - ErtsItems = - case lists:keysearch(erts, #app.name, Apps) of - {value, Erts} -> - [{erts, gen_config(Erts, InclDefaults)}]; - false -> - [] +gen_config(Sys, InclDefs) -> + {ok, do_gen_config(Sys, InclDefs)}. + +do_gen_config(#sys{root_dir = RootDir, + lib_dirs = LibDirs, + mod_cond = ModCond, + incl_cond = AppCond, + apps = Apps, + boot_rel = BootRel, + rels = Rels, + emu_name = EmuName, + profile = Profile, + incl_sys_filters = InclSysFiles, + excl_sys_filters = ExclSysFiles, + incl_app_filters = InclAppFiles, + excl_app_filters = ExclAppFiles, + incl_archive_filters = InclArchiveDirs, + excl_archive_filters = ExclArchiveDirs, + archive_opts = ArchiveOpts, + relocatable = Relocatable, + rel_app_type = RelAppType, + embedded_app_type = InclAppType, + app_file = AppFile, + debug_info = DebugInfo}, + InclDefs) -> + ErtsItems = + case lists:keyfind(erts, #app.name, Apps) of + false -> + []; + Erts -> + [{erts, do_gen_config(Erts, InclDefs)}] end, AppsItems = - [{app, A#app.name, gen_config(A, InclDefaults)} - || A <- Apps, - A#app.name =/= ?MISSING_APP, + [do_gen_config(A, InclDefs) + || A <- Apps, + A#app.name =/= ?MISSING_APP_NAME, A#app.name =/= erts, - A#app.is_included =:= true, - A#app.is_escript =/= true], - EscriptItems = [{escript, A#app.active_dir, emit(incl_cond, A#app.incl_cond, undefined, InclDefaults)} - || A <- Apps, A#app.is_escript], + not A#app.is_escript], + EscriptItems = [{escript, + A#app.active_dir, + emit(incl_cond, A#app.incl_cond, undefined, InclDefs)} + || A <- Apps, A#app.is_escript], DefaultRels = reltool_utils:default_rels(), RelsItems = - case {[{rel, R#rel.name, R#rel.vsn, gen_config(R, InclDefaults)} || R <- Rels], - [{rel, R#rel.name, R#rel.vsn, gen_config(R, InclDefaults)} || R <- DefaultRels]} of - {RI, RI} -> []; - {RI, _} -> RI - end, + [{rel, R#rel.name, R#rel.vsn, do_gen_config(R, InclDefs)} || + R <- Rels], + DefaultRelsItems = + [{rel, R#rel.name, R#rel.vsn, do_gen_config(R, InclDefs)} || + R <- DefaultRels], + RelsItems2 = + case InclDefs of + true -> RelsItems; + false -> RelsItems -- DefaultRelsItems + end, X = fun(List) -> [Re || #regexp{source = Re} <- List] end, {sys, - emit(root_dir, RootDir, code:root_dir(), InclDefaults) ++ - emit(lib_dirs, LibDirs, ?DEFAULT_LIBS, InclDefaults) ++ + emit(root_dir, RootDir, code:root_dir(), InclDefs) ++ + emit(lib_dirs, LibDirs, ?DEFAULT_LIBS, InclDefs) ++ EscriptItems ++ - emit(mod_cond, ModCond, ?DEFAULT_MOD_COND, InclDefaults) ++ - emit(incl_cond, AppCond, ?DEFAULT_INCL_COND, InclDefaults) ++ + emit(mod_cond, ModCond, ?DEFAULT_MOD_COND, InclDefs) ++ + emit(incl_cond, AppCond, ?DEFAULT_INCL_COND, InclDefs) ++ ErtsItems ++ - AppsItems ++ - emit(boot_rel, BootRel, ?DEFAULT_REL_NAME, InclDefaults) ++ - RelsItems ++ - emit(emu_name, EmuName, ?DEFAULT_EMU_NAME, InclDefaults) ++ - emit(relocatable, Relocatable, ?DEFAULT_RELOCATABLE, InclDefaults) ++ - emit(profile, Profile, ?DEFAULT_PROFILE, InclDefaults) ++ - emit(incl_sys_filters, X(InclSysFiles), ?DEFAULT_INCL_SYS_FILTERS, InclDefaults) ++ - emit(excl_sys_filters, X(ExclSysFiles), ?DEFAULT_EXCL_SYS_FILTERS, InclDefaults) ++ - emit(incl_app_filters, X(InclAppFiles), ?DEFAULT_INCL_APP_FILTERS, InclDefaults) ++ - emit(excl_app_filters, X(ExclAppFiles), ?DEFAULT_EXCL_APP_FILTERS, InclDefaults) ++ - emit(incl_archive_filters, X(InclArchiveDirs), ?DEFAULT_INCL_ARCHIVE_FILTERS, InclDefaults) ++ - emit(excl_archive_filters, X(ExclArchiveDirs), ?DEFAULT_EXCL_ARCHIVE_FILTERS, InclDefaults) ++ - emit(archive_opts, ArchiveOpts, ?DEFAULT_ARCHIVE_OPTS, InclDefaults) ++ - emit(app_type, AppType, ?DEFAULT_APP_TYPE, InclDefaults) ++ - emit(app_file, AppFile, ?DEFAULT_APP_FILE, InclDefaults) ++ - emit(debug_info, DebugInfo, ?DEFAULT_DEBUG_INFO, InclDefaults)}; -gen_config(#app{name = _Name, - mod_cond = ModCond, - incl_cond = AppCond, - debug_info = DebugInfo, - app_file = AppFile, - incl_app_filters = InclAppFiles, - excl_app_filters = ExclAppFiles, - incl_archive_filters = InclArchiveDirs, - excl_archive_filters = ExclArchiveDirs, - archive_opts = ArchiveOpts, - use_selected_vsn = UseSelected, - vsn = Vsn, - mods = Mods}, - InclDefaults) -> - emit(mod_cond, ModCond, undefined, InclDefaults) ++ - emit(incl_cond, AppCond, undefined, InclDefaults) ++ - emit(debug_info, DebugInfo, undefined, InclDefaults) ++ - emit(app_file, AppFile, undefined, InclDefaults) ++ - emit(incl_app_filters, InclAppFiles, undefined, InclDefaults) ++ - emit(excl_app_filters, ExclAppFiles, undefined, InclDefaults) ++ - emit(incl_archive_filters, InclArchiveDirs, undefined, InclDefaults) ++ - emit(excl_archive_filters, ExclArchiveDirs, undefined, InclDefaults) ++ - emit(archive_opts, ArchiveOpts, undefined, InclDefaults) ++ - emit(vsn, Vsn, undefined, InclDefaults orelse UseSelected =/= true) ++ - [{mod, M#mod.name, gen_config(M, InclDefaults)} || M <- Mods, M#mod.is_included =:= true]; -gen_config(#mod{name = _Name, - incl_cond = AppCond, - debug_info = DebugInfo}, - InclDefaults) -> - emit(incl_cond, AppCond, undefined, InclDefaults) ++ - emit(debug_info, DebugInfo, undefined, InclDefaults); -gen_config(#rel{name = _Name, - vsn = _Vsn, - rel_apps = RelApps}, - InclDefaults) -> - [gen_config(RA, InclDefaults) || RA <- RelApps]; -gen_config(#rel_app{name = Name, - app_type = Type, - incl_apps = InclApps}, - _InclDefaults) -> + lists:flatten(AppsItems) ++ + emit(boot_rel, BootRel, ?DEFAULT_REL_NAME, InclDefs) ++ + RelsItems2 ++ + emit(emu_name, EmuName, ?DEFAULT_EMU_NAME, InclDefs) ++ + emit(relocatable, Relocatable, ?DEFAULT_RELOCATABLE, InclDefs) ++ + emit(profile, Profile, ?DEFAULT_PROFILE, InclDefs) ++ + emit(incl_sys_filters, X(InclSysFiles), reltool_utils:choose_default(incl_sys_filters, Profile, InclDefs), InclDefs) ++ + emit(excl_sys_filters, X(ExclSysFiles), reltool_utils:choose_default(excl_sys_filters, Profile, InclDefs), InclDefs) ++ + emit(incl_app_filters, X(InclAppFiles), reltool_utils:choose_default(incl_app_filters, Profile, InclDefs), InclDefs) ++ + emit(excl_app_filters, X(ExclAppFiles), reltool_utils:choose_default(excl_app_filters, Profile, InclDefs), InclDefs) ++ + emit(incl_archive_filters, X(InclArchiveDirs), ?DEFAULT_INCL_ARCHIVE_FILTERS, InclDefs) ++ + emit(excl_archive_filters, X(ExclArchiveDirs), ?DEFAULT_EXCL_ARCHIVE_FILTERS, InclDefs) ++ + emit(archive_opts, ArchiveOpts, ?DEFAULT_ARCHIVE_OPTS, InclDefs) ++ + emit(rel_app_type, RelAppType, ?DEFAULT_REL_APP_TYPE, InclDefs) ++ + emit(embedded_app_type, InclAppType, reltool_utils:choose_default(embedded_app_type, Profile, InclDefs), InclDefs) ++ + emit(app_file, AppFile, ?DEFAULT_APP_FILE, InclDefs) ++ + emit(debug_info, DebugInfo, ?DEFAULT_DEBUG_INFO, InclDefs)}; +do_gen_config(#app{name = Name, + mod_cond = ModCond, + incl_cond = AppCond, + debug_info = DebugInfo, + app_file = AppFile, + incl_app_filters = InclAppFiles, + excl_app_filters = ExclAppFiles, + incl_archive_filters = InclArchiveDirs, + excl_archive_filters = ExclArchiveDirs, + archive_opts = ArchiveOpts, + use_selected_vsn = UseSelected, + vsn = Vsn, + mods = Mods, + is_included = IsIncl}, + InclDefs) -> + AppConfig = + [ + emit(mod_cond, ModCond, undefined, InclDefs), + emit(incl_cond, AppCond, undefined, InclDefs), + emit(debug_info, DebugInfo, undefined, InclDefs), + emit(app_file, AppFile, undefined, InclDefs), + emit(incl_app_filters, InclAppFiles, undefined, InclDefs), + emit(excl_app_filters, ExclAppFiles, undefined, InclDefs), + emit(incl_archive_filters, InclArchiveDirs, undefined, InclDefs), + emit(excl_archive_filters, ExclArchiveDirs, undefined, InclDefs), + emit(archive_opts, ArchiveOpts, undefined, InclDefs), + if + IsIncl, InclDefs -> [{vsn, Vsn}]; + UseSelected -> [{vsn, Vsn}]; + true -> [] + end, + [do_gen_config(M, InclDefs) || M <- Mods] + ], + case lists:flatten(AppConfig) of + FlatAppConfig when FlatAppConfig =/= []; IsIncl -> + [{app, Name, FlatAppConfig}]; + [] -> + [] + end; +do_gen_config(#mod{name = Name, + incl_cond = AppCond, + debug_info = DebugInfo, + is_included = IsIncl}, + InclDefs) -> + ModConfig = + [ + emit(incl_cond, AppCond, undefined, InclDefs), + emit(debug_info, DebugInfo, undefined, InclDefs) + ], + case lists:flatten(ModConfig) of + FlatModConfig when FlatModConfig =/= []; IsIncl -> + [{mod, Name, FlatModConfig}]; + _ -> + [] + end; +do_gen_config(#rel{name = _Name, + vsn = _Vsn, + rel_apps = RelApps}, + InclDefs) -> + [do_gen_config(RA, InclDefs) || RA <- RelApps]; +do_gen_config(#rel_app{name = Name, + app_type = Type, + incl_apps = InclApps}, + _InclDefs) -> case {Type, InclApps} of {undefined, []} -> Name; {undefined, _} -> {Name, InclApps}; {_, []} -> {Name, Type}; {_, _} -> {Name, Type, InclApps} end; -gen_config({Tag, Val}, InclDefaults) -> - emit(Tag, Val, undefined, InclDefaults); -gen_config([], _InclDefaults) -> +do_gen_config({Tag, Val}, InclDefs) -> + emit(Tag, Val, undefined, InclDefs); +do_gen_config([], _InclDefs) -> []; -gen_config([H | T], InclDefaults) -> - lists:flatten([gen_config(H, InclDefaults), gen_config(T, InclDefaults)]). +do_gen_config([H | T], InclDefs) -> + lists:flatten([do_gen_config(H, InclDefs), do_gen_config(T, InclDefs)]). -emit(Tag, Val, Default, InclDefaults) -> +emit(Tag, Val, Default, InclDefs) -> + %% io:format("~p(~p):\n\t~p\n\t~p\n", + %% [Tag, Val =/= Default, Val, Default]), if Val == undefined -> []; - InclDefaults -> [{Tag, Val}]; + InclDefs -> [{Tag, Val}]; Val =/= Default -> [{Tag, Val}]; true -> [] - end. + end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Generate the contents of an app file %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -gen_app(#app{name = Name, +gen_app(#app{name = Name, info = #app_info{description = Desc, id = Id, vsn = Vsn, @@ -231,22 +270,126 @@ gen_app(#app{name = Name, %% Generate the contents of a rel file %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -gen_rel(#rel{name = RelName, vsn = RelVsn, rel_apps = RelApps}, - #sys{apps = Apps}) -> - {value, Erts} = lists:keysearch(erts, #app.name, Apps), - {release, - {RelName, RelVsn}, - {erts, Erts#app.vsn}, - [app_to_rel(RA, Apps ) || RA <- RelApps]}. +gen_rel(Rel, Sys) -> + try + MergedApps = merge_apps(Rel, Sys), + {ok, do_gen_rel(Rel, Sys, MergedApps)} + catch + throw:{error, Text} -> + {error, Text} + end. -app_to_rel(#rel_app{name = Name, app_type = Type, incl_apps = InclApps}, Apps) -> - {value, #app{vsn = Vsn}} = lists:keysearch(Name, #app.name, Apps), +do_gen_rel(#rel{name = RelName, vsn = RelVsn}, + #sys{apps = Apps}, + MergedApps) -> + ErtsName = erts, + case lists:keysearch(ErtsName, #app.name, Apps) of + {value, Erts} -> + {release, + {RelName, RelVsn}, + {ErtsName, Erts#app.vsn}, + [strip_rel_info(App) || App <- MergedApps]}; + false -> + reltool_utils:throw_error("Mandatory application ~p is " + "not included", + [ErtsName]) + end. + +strip_rel_info(#app{name = Name, + vsn = Vsn, + app_type = Type, + info = #app_info{incl_apps = InclApps}}) + when Type =/= undefined -> case {Type, InclApps} of - {undefined, []} -> {Name, Vsn}; - {undefined, _} -> {Name, Vsn, InclApps}; + {permanent, []} -> {Name, Vsn}; + {permanent, _} -> {Name, Vsn, InclApps}; {_, []} -> {Name, Vsn, Type}; {_, _} -> {Name, Vsn, Type, InclApps} - end. + end. + +merge_apps(#rel{name = RelName, + rel_apps = RelApps}, + #sys{apps = Apps, + rel_app_type = RelAppType, + embedded_app_type = EmbAppType}) -> + Mandatory = [kernel, stdlib], + MergedApps = do_merge_apps(RelName, Mandatory, Apps, permanent, []), + MergedApps2 = do_merge_apps(RelName, RelApps, Apps, RelAppType, MergedApps), + Embedded = + [A#app.name || A <- Apps, + EmbAppType =/= undefined, + A#app.is_included, + A#app.name =/= erts, + 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). + +do_merge_apps(RelName, [#rel_app{name = Name} = RA | RelApps], Apps, RelAppType, Acc) -> + case is_already_merged(Name, RelApps, Acc) of + true -> + do_merge_apps(RelName, RelApps, Apps, RelAppType, Acc); + false -> + {value, App} = lists:keysearch(Name, #app.name, Apps), + MergedApp = merge_app(RelName, RA, RelAppType, App), + MoreNames = (MergedApp#app.info)#app_info.applications, + Acc2 = [MergedApp | Acc], + do_merge_apps(RelName, MoreNames ++ RelApps, Apps, RelAppType, Acc2) + end; +do_merge_apps(RelName, [Name | RelApps], Apps, RelAppType, Acc) -> + case is_already_merged(Name, RelApps, Acc) of + true -> + do_merge_apps(RelName, RelApps, Apps, RelAppType, Acc); + false -> + RelApp = init_rel_app(Name, Apps), + 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}. + +merge_app(RelName, + #rel_app{name = Name, + app_type = Type, + incl_apps = InclApps}, + RelAppType, + App) -> + Type2 = + case {Type, App#app.app_type} of + {undefined, undefined} -> RelAppType; + {undefined, AppAppType} -> AppAppType; + {_, _} -> Type + end, + Info = App#app.info, + case InclApps -- Info#app_info.incl_apps of + [] -> + App#app{app_type = Type2, info = Info#app_info{incl_apps = InclApps}}; + BadIncl -> + reltool_utils:throw_error("~p: These applications are " + "used by release ~s but are " + "missing as included_applications " + "in the app file: ~p", + [Name, RelName, BadIncl]) + end. + +is_already_merged(Name, [Name | _], _MergedApps) -> + true; +is_already_merged(Name, [#rel_app{name = Name} | _], _MergedApps) -> + true; +is_already_merged(Name, [_ | RelApps], MergedApps) -> + is_already_merged(Name, RelApps, MergedApps); +is_already_merged(Name, [], [#app{name = Name} | _MergedApps]) -> + true; +is_already_merged(Name, [] = RelApps, [_ | MergedApps]) -> + is_already_merged(Name, RelApps, MergedApps); +is_already_merged(_Name, [], []) -> + false. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Generate the contents of a boot file @@ -261,25 +404,24 @@ gen_boot({script, {_, _}, _} = Script) -> gen_script(Rel, Sys, PathFlag, Variables) -> try - do_gen_script(Rel, Sys, PathFlag, Variables) - catch + MergedApps = merge_apps(Rel, Sys), + do_gen_script(Rel, Sys, MergedApps, PathFlag, Variables) + catch throw:{error, Text} -> {error, Text} end. -do_gen_script(#rel{name = RelName, vsn = RelVsn, rel_apps = RelApps}, - #sys{apps = Apps, app_type = DefaultType}, +do_gen_script(#rel{name = RelName, vsn = RelVsn}, + #sys{apps = Apps}, + MergedApps, PathFlag, Variables) -> {value, Erts} = lists:keysearch(erts, #app.name, Apps), Preloaded = [Mod#mod.name || Mod <- Erts#app.mods], Mandatory = mandatory_modules(), Early = Mandatory ++ Preloaded, - MergedApps = [merge_app(RA, Apps, DefaultType) || RA <- RelApps], - SortedApps = sort_apps(MergedApps), - {value, KernelApp} = lists:keysearch(kernel, #app.name, SortedApps), - - InclApps = lists:append([I || #app{info = #app_info{incl_apps = I}} <- SortedApps]), + {value, KernelApp} = lists:keysearch(kernel, #app.name, MergedApps), + InclApps = [I || #app{info = #app_info{incl_apps = I}} <- MergedApps], %% Create the script DeepList = @@ -289,32 +431,32 @@ do_gen_script(#rel{name = RelName, vsn = RelVsn, rel_apps = RelApps}, {progress, preloaded}, %% Load mandatory modules - {path, create_mandatory_path(SortedApps, PathFlag, Variables)}, + {path, create_mandatory_path(MergedApps, PathFlag, Variables)}, {primLoad, lists:sort(Mandatory)}, {kernel_load_completed}, {progress, kernel_load_completed}, %% Load remaining modules - [load_app_mods(A, Early, PathFlag, Variables) || A <- SortedApps], + [load_app_mods(A, Early, PathFlag, Variables) || A <- MergedApps], {progress, modules_loaded}, %% Start kernel processes - {path, create_path(SortedApps, PathFlag, Variables)}, + {path, create_path(MergedApps, PathFlag, Variables)}, kernel_processes(gen_app(KernelApp)), {progress, init_kernel_started}, %% Load applications [{apply, {application, load, [gen_app(A)]}} || - A = #app{name = Name, app_type = Type} <- SortedApps, + A = #app{name = Name, app_type = Type} <- MergedApps, Name =/= kernel, Type =/= none], {progress, applications_loaded}, %% Start applications [{apply, {application, start_boot, [Name, Type]}} || - #app{name = Name, app_type = Type} <- SortedApps, - Type =/= none, - Type =/= load, + #app{name = Name, app_type = Type} <- MergedApps, + Type =/= none, + Type =/= load, not lists:member(Name, InclApps)], %% Apply user specific customizations @@ -323,24 +465,6 @@ do_gen_script(#rel{name = RelName, vsn = RelVsn, rel_apps = RelApps}, ], {ok, {script, {RelName, RelVsn}, lists:flatten(DeepList)}}. -merge_app(#rel_app{name = Name, app_type = Type, incl_apps = RelIncl}, Apps, DefaultType) -> - {value, App} = lists:keysearch(Name, #app.name, Apps), - Type2 = - case {Type, App#app.app_type} of - {undefined, undefined} -> DefaultType; - {undefined, AppType} -> AppType; - {_, _} -> Type - end, - Info = App#app.info, - case RelIncl -- Info#app_info.incl_apps of - [] -> - App#app{app_type = Type2, info = Info#app_info{incl_apps = RelIncl}}; - BadIncl -> - reltool_utils:throw_error("~p: These applications are missing as " - "included_applications in the app file: ~p\n", - [Name, BadIncl]) - end. - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% load_app_mods(#app{mods = Mods} = App, Mand, PathFlag, Variables) -> @@ -359,7 +483,7 @@ load_app_mods(#app{mods = Mods} = App, Mand, PathFlag, Variables) -> Subs -> [{Subs, [M]}|[{Last,Acc}|Rest]] end - end, + end, [{[], []}], PartNames), @@ -381,17 +505,25 @@ load_app_mods(#app{mods = Mods} = App, Mand, PathFlag, Variables) -> %% Mod. by mbj %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -sort_apps(Apps) -> +sort_apps(Apps) -> sort_apps(Apps, [], [], []). -sort_apps([#app{name = Name, info = Info} = App | Apps], Missing, Circular, Visited) -> - {Uses, Apps1, NotFnd1} = find_all(Name, Info#app_info.applications, Apps, Visited, [], []), - {Incs, Apps2, NotFnd2} = find_all(Name, lists:reverse(Info#app_info.incl_apps), - Apps1, Visited, [], []), - +sort_apps([#app{name = Name, info = Info} = App | Apps], + Missing, + Circular, + Visited) -> + {Uses, Apps1, NotFnd1} = + find_all(Name, Info#app_info.applications, Apps, Visited, [], []), + {Incs, Apps2, NotFnd2} = + find_all(Name, + lists:reverse(Info#app_info.incl_apps), + Apps1, + Visited, + [], + []), Missing1 = NotFnd1 ++ NotFnd2 ++ Missing, case Uses ++ Incs of - [] -> + [] -> %% No more app that must be started before this one is %% found; they are all already taken care of (and present %% in Visited list) @@ -400,9 +532,9 @@ sort_apps([#app{name = Name, info = Info} = App | Apps], Missing, Circular, Visi %% The apps in L must be started before the app. %% Check if we have already taken care of some app in L, %% in that case we have a circular dependency. - NewCircular = [N1 || N1 <- L, N2 <- Visited, N1 =:= N2], - Circular1 = case NewCircular of - [] -> Circular; + NewCircular = [N || #app{name = N} <- L, N2 <- Visited, N =:= N2], + Circular1 = case NewCircular of + [] -> Circular; _ -> [Name | NewCircular] ++ Circular end, %% L must be started before N, try again, with all apps @@ -414,41 +546,68 @@ sort_apps([], [], [], _) -> []; sort_apps([], Missing, [], _) -> %% this has already been checked before, but as we have the info... - reltool_utils:throw_error("Undefined applications: ~p\n", [make_set(Missing)]); + reltool_utils:throw_error("Undefined applications: ~p", + [make_set(Missing)]); sort_apps([], [], Circular, _) -> - reltool_utils:throw_error("Circular dependencies: ~p\n", [make_set(Circular)]); + reltool_utils:throw_error("Circular dependencies: ~p", + [make_set(Circular)]); sort_apps([], Missing, Circular, _) -> - reltool_utils:throw_error("Circular dependencies: ~p\n" + reltool_utils:throw_error("Circular dependencies: ~p" "Undefined applications: ~p\n", [make_set(Circular), make_set(Missing)]). find_all(CheckingApp, [Name | Names], Apps, Visited, Found, NotFound) -> - case lists:keysearch(Name, #app.name, Apps) of - {value, #app{info = Info} = App} -> - %% It is OK to have a dependecy like + case lists:keyfind(Name, #app.name, Apps) of + #app{info = Info} = App -> + %% It is OK to have a dependency like %% X includes Y, Y uses X. case lists:member(CheckingApp, Info#app_info.incl_apps) of true -> case lists:member(Name, Visited) of true -> - find_all(CheckingApp, Names, Apps, Visited, Found, NotFound); + find_all(CheckingApp, + Names, + Apps, + Visited, + Found, + NotFound); false -> - find_all(CheckingApp, Names, Apps, Visited, Found, [Name | NotFound]) + find_all(CheckingApp, + Names, + Apps, + Visited, + Found, + [Name | NotFound]) end; false -> - find_all(CheckingApp, Names, Apps -- [App], Visited, [App|Found], NotFound) + find_all(CheckingApp, + Names, + Apps -- [App], + Visited, + [App|Found], + NotFound) end; false -> case lists:member(Name, Visited) of true -> - find_all(CheckingApp, Names, Apps, Visited, Found, NotFound); + find_all(CheckingApp, + Names, + Apps, + Visited, + Found, + NotFound); false -> - find_all(CheckingApp, Names, Apps, Visited, Found, [Name|NotFound]) + find_all(CheckingApp, + Names, + Apps, + Visited, + Found, + [Name|NotFound]) end end; find_all(_CheckingApp, [], Apps, _Visited, Found, NotFound) -> {Found, Apps, NotFound}. - + del_apps([Name | Names], Apps) -> del_apps(Names, lists:keydelete(Name, #app.name, Apps)); del_apps([], Apps) -> @@ -470,7 +629,9 @@ create_path(Apps, PathFlag, Variables) -> %% (The otp_build flag is only used for OTP internal system make) cr_path(#app{label = Label}, true, []) -> filename:join(["$ROOT", "lib", Label, "ebin"]); -cr_path(#app{name = Name, vsn = Vsn, label = Label, active_dir = Dir}, true, Variables) -> +cr_path(#app{name = Name, vsn = Vsn, label = Label, active_dir = Dir}, + true, + Variables) -> Tail = [Label, "ebin"], case variable_dir(Dir, atom_to_list(Name), Vsn, Variables) of {ok, VarDir} -> @@ -542,7 +703,7 @@ gen_rel_files(Sys, TargetDir) -> try Spec = spec_rel_files(Sys), eval_spec(Spec, Sys#sys.root_dir, TargetDir) - catch + catch throw:{error, Text} -> {error, Text} end. @@ -550,14 +711,15 @@ gen_rel_files(Sys, TargetDir) -> spec_rel_files(#sys{rels = Rels} = Sys) -> lists:append([do_spec_rel_files(R, Sys) || R <- Rels]). -do_spec_rel_files(#rel{name = Name} = Rel, Sys) -> - RelFile = Name ++ ".rel", - ScriptFile = Name ++ ".script", - BootFile = Name ++ ".boot", - GenRel = gen_rel(Rel, Sys), +do_spec_rel_files(#rel{name = RelName} = Rel, Sys) -> + RelFile = RelName ++ ".rel", + ScriptFile = RelName ++ ".script", + BootFile = RelName ++ ".boot", + MergedApps = merge_apps(Rel, Sys), + GenRel = do_gen_rel(Rel, Sys, MergedApps), PathFlag = true, Variables = [], - {ok, Script} = do_gen_script(Rel, Sys, PathFlag, Variables), + {ok, Script} = do_gen_script(Rel, Sys, MergedApps, PathFlag, Variables), {ok, BootBin} = gen_boot(Script), Date = date(), Time = time(), @@ -579,15 +741,15 @@ gen_target(Sys, TargetDir) -> try Spec = do_gen_spec(Sys), eval_spec(Spec, Sys#sys.root_dir, TargetDir) - catch + catch throw:{error, Text} -> {error, Text} end. - + gen_spec(Sys) -> try {ok, do_gen_spec(Sys)} - catch + catch throw:{error, Text} -> {error, Text} end. @@ -598,20 +760,20 @@ do_gen_spec(#sys{root_dir = RootDir, relocatable = Relocatable, apps = Apps} = Sys) -> {create_dir, _, SysFiles} = spec_dir(RootDir), - {ExclRegexps2, SysFiles2} = strip_sys_files(Relocatable, SysFiles, Apps, ExclRegexps), + {ExclRegexps2, SysFiles2} = + strip_sys_files(Relocatable, SysFiles, Apps, ExclRegexps), RelFiles = spec_rel_files(Sys), - {InclRegexps2, BinFiles} = spec_bin_files(Sys, SysFiles, SysFiles2, RelFiles, InclRegexps), + {InclRegexps2, BinFiles} = + spec_bin_files(Sys, SysFiles, SysFiles2, RelFiles, InclRegexps), LibFiles = spec_lib_files(Sys), {BootVsn, StartFile} = spec_start_file(Sys), SysFiles3 = [ - {create_dir, "releases", + {create_dir, "releases", [StartFile, {create_dir,BootVsn, RelFiles}]}, {create_dir, "bin", BinFiles} ] ++ SysFiles2, - %% io:format("InclRegexps2: ~p\n", [InclRegexps2]), - %% io:format("ExclRegexps2: ~p\n", [ExclRegexps2]), SysFiles4 = filter_spec(SysFiles3, InclRegexps2, ExclRegexps2), SysFiles5 = SysFiles4 ++ [{create_dir, "lib", LibFiles}], check_sys(["bin", "erts", "lib"], SysFiles5), @@ -621,24 +783,27 @@ strip_sys_files(Relocatable, SysFiles, Apps, ExclRegexps) -> ExclRegexps2 = case Relocatable of true -> - ExtraExcl = ["^erts.*/bin/.*src$"], - reltool_utils:decode_regexps(excl_sys_filters, {add, ExtraExcl}, ExclRegexps); + ExtraExcl = ["^erts.*/bin/.*src\$"], + reltool_utils:decode_regexps(excl_sys_filters, + {add, ExtraExcl}, + ExclRegexps); false -> ExclRegexps end, {value, Erts} = lists:keysearch(erts, #app.name, Apps), FilterErts = - fun(Spec) -> - File = element(2, Spec), - case lists:prefix("erts", File) of - true -> - if - File =:= Erts#app.label -> - replace_dyn_erl(Relocatable, Spec); - true -> - false - end; - false -> + fun(Spec) -> + File = element(2, Spec), + case File of + "erts" -> + reltool_utils:throw_error("This system is not installed. " + "The directory ~s is missing.", + [Erts#app.label]); + _ when File =:= Erts#app.label -> + replace_dyn_erl(Relocatable, Spec); + "erts-" ++ _ -> + false; + _ -> true end end, @@ -651,7 +816,8 @@ strip_sys_files(Relocatable, SysFiles, Apps, ExclRegexps) -> replace_dyn_erl(false, _ErtsSpec) -> true; replace_dyn_erl(true, {create_dir, ErtsDir, ErtsFiles}) -> - [{create_dir, _, BinFiles}] = safe_lookup_spec("bin", ErtsFiles), + [{create_dir, _, BinFiles}] = + safe_lookup_spec("bin", ErtsFiles), case lookup_spec("dyn_erl", BinFiles) of [] -> case lookup_spec("erl.ini", BinFiles) of @@ -660,7 +826,11 @@ replace_dyn_erl(true, {create_dir, ErtsDir, ErtsFiles}) -> [{copy_file, ErlIni}] -> %% Remove Windows .ini file BinFiles2 = lists:keydelete(ErlIni, 2, BinFiles), - ErtsFiles2 = lists:keyreplace("bin", 2, ErtsFiles, {create_dir, "bin", BinFiles2}), + ErtsFiles2 = + lists:keyreplace("bin", + 2, + ErtsFiles, + {create_dir, "bin", BinFiles2}), {true, {create_dir, ErtsDir, ErtsFiles2}} end; [{copy_file, DynErlExe}] -> @@ -668,42 +838,62 @@ replace_dyn_erl(true, {create_dir, ErtsDir, ErtsFiles}) -> ErlExe = "erl" ++ filename:extension(DynErlExe), BinFiles2 = lists:keydelete(DynErlExe, 2, BinFiles), DynErlExe2 = filename:join([ErtsDir, "bin", DynErlExe]), - BinFiles3 = lists:keyreplace(ErlExe, 2, BinFiles2, {copy_file, ErlExe, DynErlExe2}), - ErtsFiles2 = lists:keyreplace("bin", 2, ErtsFiles, {create_dir, "bin", BinFiles3}), + BinFiles3 = lists:keyreplace(ErlExe, + 2, + BinFiles2, + {copy_file, ErlExe, DynErlExe2}), + ErtsFiles2 = lists:keyreplace("bin", + 2, + ErtsFiles, + {create_dir, "bin", BinFiles3}), {true, {create_dir, ErtsDir, ErtsFiles2}} end. spec_bin_files(Sys, AllSysFiles, StrippedSysFiles, RelFiles, InclRegexps) -> - [{create_dir, ErtsLabel, ErtsFiles}] = safe_lookup_spec("erts", StrippedSysFiles), + [{create_dir, ErtsLabel, ErtsFiles}] = + safe_lookup_spec("erts", StrippedSysFiles), [{create_dir, _, BinFiles}] = safe_lookup_spec("bin", ErtsFiles), ErtsBin = filename:join([ErtsLabel, "bin"]), Escripts = spec_escripts(Sys, ErtsBin, BinFiles), Map = fun({copy_file, File}) -> {copy_file, File, filename:join([ErtsBin, File])}; ({copy_file, NewFile, OldFile}) -> - {_, OldFile2} = abs_to_rel_path(ErtsBin, filename:join([ErtsBin, OldFile])), + {_, OldFile2} = + abs_to_rel_path(ErtsBin, + filename:join([ErtsBin, OldFile])), {copy_file, NewFile, OldFile2} end, %% Do only copy those bin files from erts/bin that also exists in bin [{create_dir, _, OldBinFiles}] = safe_lookup_spec("bin", AllSysFiles), GoodNames = [F || {copy_file, F} <- OldBinFiles, - not lists:suffix(".boot", F), + not lists:suffix(".boot", F), not lists:suffix(".script", F)], - BinFiles2 = [Map(S) || S <- BinFiles, lists:member(element(2, S), GoodNames)], + BinFiles2 = [Map(S) || S <- BinFiles, + lists:member(element(2, S), GoodNames)], BootFiles = [F || F <- RelFiles, lists:suffix(".boot", element(2, F))], - [{write_file, _, BootRel}] = safe_lookup_spec(Sys#sys.boot_rel ++ ".boot", BootFiles), - BootFiles2 = lists:keystore("start.boot", 2, BootFiles, {write_file, "start.boot", BootRel}), - MakeRegexp = fun(File) -> "^bin/" ++ element(2, File) ++ "(|.escript)$" end, + [{write_file, _, BootRel}] = + safe_lookup_spec(Sys#sys.boot_rel ++ ".boot", BootFiles), + BootFiles2 = lists:keystore("start.boot", + 2, + BootFiles, + {write_file, "start.boot", BootRel}), + MakeRegexp = + fun(File) -> "^bin/" ++ element(2, File) ++ "(|.escript)\$" end, ExtraIncl = lists:map(MakeRegexp, Escripts), - InclRegexps2 = reltool_utils:decode_regexps(incl_sys_filters, {add, ExtraIncl}, InclRegexps), + InclRegexps2 = reltool_utils:decode_regexps(incl_sys_filters, + {add, ExtraIncl}, + InclRegexps), {InclRegexps2, Escripts ++ BinFiles2 ++ BootFiles2}. spec_escripts(#sys{apps = Apps}, ErtsBin, BinFiles) -> - Filter = fun(#app{is_escript = IsEscript, is_included = IsIncl, - is_pre_included = IsPre, name = Name, active_dir = File}) -> + Filter = fun(#app{is_escript = IsEscript, + is_included = IsIncl, + is_pre_included = IsPre, + name = Name, + active_dir = File}) -> if - Name =:= ?MISSING_APP -> + Name =:= ?MISSING_APP_NAME -> false; not IsEscript -> false; @@ -722,15 +912,15 @@ do_spec_escript(File, ErtsBin, BinFiles) -> ExeExt = filename:extension(EscriptExe), [{copy_file, Base ++ EscriptExt, File}, {copy_file, Base ++ ExeExt, filename:join([ErtsBin, EscriptExe])}]. - + check_sys(Mandatory, SysFiles) -> lists:foreach(fun(M) -> do_check_sys(M, SysFiles) end, Mandatory). do_check_sys(Prefix, Specs) -> - %%io:format("Prefix: ~p\n", [Prefix]), case lookup_spec(Prefix, Specs) of [] -> - reltool_utils:throw_error("Mandatory system directory ~s is not included", + reltool_utils:throw_error("Mandatory system directory ~s " + "is not included", [Prefix]); _ -> ok @@ -750,7 +940,9 @@ lookup_spec(Prefix, Specs) -> safe_lookup_spec(Prefix, Specs) -> case lookup_spec(Prefix, Specs) of [] -> - reltool_utils:throw_error("Mandatory system file ~s is not included", [Prefix]); + %% io:format("lookup fail ~s:\n\t~p\n", [Prefix, Specs]), + reltool_utils:throw_error("Mandatory system file ~s is " + "not included", [Prefix]); Match -> Match end. @@ -763,7 +955,7 @@ spec_lib_files(#sys{apps = Apps} = Sys) -> Filter = fun(#app{is_escript = IsEscript, is_included = IsIncl, is_pre_included = IsPre, name = Name}) -> if - Name =:= ?MISSING_APP -> + Name =:= ?MISSING_APP_NAME -> false; IsEscript -> false; @@ -780,7 +972,8 @@ spec_lib_files(#sys{apps = Apps} = Sys) -> check_apps([Mandatory | Names], Apps) -> case lists:keymember(Mandatory, #app.name, Apps) of false -> - reltool_utils:throw_error("Mandatory application ~p is not included in ~p", + reltool_utils:throw_error("Mandatory application ~p is " + "not included in ~p", [Mandatory, Apps]); true -> check_apps(Names, Apps) @@ -791,10 +984,10 @@ check_apps([], _) -> spec_app(#app{name = Name, mods = Mods, active_dir = SourceDir, - incl_app_filters = AppInclRegexps, - excl_app_filters = AppExclRegexps} = App, - #sys{incl_app_filters = SysInclRegexps, - excl_app_filters = SysExclRegexps, + incl_app_filters = AppInclRegexps, + excl_app_filters = AppExclRegexps} = App, + #sys{incl_app_filters = SysInclRegexps, + excl_app_filters = SysExclRegexps, debug_info = SysDebugInfo} = Sys) -> %% List files recursively {create_dir, _, AppFiles} = spec_dir(SourceDir), @@ -804,8 +997,12 @@ spec_app(#app{name = Name, EbinDir = filename:join([SourceDir, "ebin"]), OptAppUpFileSpec = spec_opt_copy_file(EbinDir, AppUpFilename), OptAppFileSpec = spec_app_file(App, Sys, EbinDir), - ModSpecs = [spec_mod(M, SysDebugInfo) || M <- Mods, M#mod.is_included, M#mod.exists], - NewEbin = {create_dir, "ebin", OptAppUpFileSpec ++ OptAppFileSpec ++ ModSpecs}, + ModSpecs = [spec_mod(M, SysDebugInfo) || M <- Mods, + M#mod.is_included, + M#mod.exists], + NewEbin = {create_dir, + "ebin", + OptAppUpFileSpec ++ OptAppFileSpec ++ ModSpecs}, AppFiles2 = lists:keystore("ebin", 2, AppFiles, NewEbin), %% Apply file filter @@ -826,24 +1023,32 @@ spec_archive(#app{label = Label, excl_archive_filters = SysExclArchiveDirs, archive_opts = SysArchiveOpts}, Files) -> - InclArchiveDirs = reltool_utils:default_val(AppInclArchiveDirs, SysInclArchiveDirs), - ExclArchiveDirs = reltool_utils:default_val(AppExclArchiveDirs, SysExclArchiveDirs), - ArchiveOpts = reltool_utils:default_val(AppArchiveOpts, SysArchiveOpts), + InclArchiveDirs = + reltool_utils:default_val(AppInclArchiveDirs, SysInclArchiveDirs), + ExclArchiveDirs = + reltool_utils:default_val(AppExclArchiveDirs, SysExclArchiveDirs), + ArchiveOpts = + reltool_utils:default_val(AppArchiveOpts, SysArchiveOpts), Match = fun(F) -> match(element(2, F), InclArchiveDirs, ExclArchiveDirs) end, case lists:filter(Match, Files) of [] -> %% Nothing to archive [spec_create_dir(RootDir, SourceDir, Label, Files)]; ArchiveFiles -> - OptDir = + OptDir = case Files -- ArchiveFiles of [] -> []; ExternalFiles -> - [spec_create_dir(RootDir, SourceDir, Label, ExternalFiles)] + [spec_create_dir(RootDir, + SourceDir, + Label, + ExternalFiles)] end, - ArchiveOpts = reltool_utils:default_val(AppArchiveOpts, SysArchiveOpts), - ArchiveDir = spec_create_dir(RootDir, SourceDir, Label, ArchiveFiles), + ArchiveOpts = + reltool_utils:default_val(AppArchiveOpts, SysArchiveOpts), + ArchiveDir = + spec_create_dir(RootDir, SourceDir, Label, ArchiveFiles), [{archive, Label ++ ".ez", ArchiveOpts, [ArchiveDir]} | OptDir] end. @@ -854,15 +1059,17 @@ spec_dir(Dir) -> case erl_prim_loader:list_dir(Dir) of {ok, Files} -> %% Directory - {create_dir, Base, [spec_dir(filename:join([Dir, F])) || F <- Files]}; + {create_dir, + Base, + [spec_dir(filename:join([Dir, F])) || F <- Files]}; error -> - reltool_utils:throw_error("list dir ~s failed\n", [Dir]) + reltool_utils:throw_error("list dir ~s failed", [Dir]) end; {ok, #file_info{type = regular}} -> %% Plain file {copy_file, Base}; _ -> - reltool_utils:throw_error("read file info ~s failed\n", [Dir]) + reltool_utils:throw_error("read file info ~s failed", [Dir]) end. spec_mod(Mod, DebugInfo) -> @@ -895,7 +1102,7 @@ spec_app_file(#app{name = Name, App2 = App#app{info = Info#app_info{modules = ModNames}}, Contents = gen_app(App2), AppIoList = io_lib:format("%% app generated at ~w ~w\n~p.\n\n", - [date(), time(), Contents]), + [date(), time(), Contents]), [{write_file, AppFilename, AppIoList}]; all -> %% Include all included modules @@ -904,13 +1111,14 @@ spec_app_file(#app{name = Name, App2 = App#app{info = Info#app_info{modules = ModNames}}, Contents = gen_app(App2), AppIoList = io_lib:format("%% app generated at ~w ~w\n~p.\n\n", - [date(), time(), Contents]), + [date(), time(), Contents]), [{write_file, AppFilename, AppIoList}] - + end. spec_opt_copy_file(DirName, BaseName) -> - case filelib:is_regular(filename:join([DirName, BaseName]), erl_prim_loader) of + case filelib:is_regular(filename:join([DirName, BaseName]), + erl_prim_loader) of true -> [{copy_file, BaseName}]; false -> [] end. @@ -949,14 +1157,17 @@ eval_spec(Spec, SourceDir, TargetDir) -> false -> {error, TargetDir2 ++ ": " ++ file:format_error(enoent)} end - catch + catch throw:{error, Text} -> cleanup_spec(Spec, TargetDir2), {error, Text} end. do_eval_spec(List, OrigSourceDir, SourceDir, TargetDir) when is_list(List) -> - lists:foreach(fun(F) -> do_eval_spec(F, OrigSourceDir, SourceDir, TargetDir) end, List); + lists:foreach(fun(F) -> + do_eval_spec(F, OrigSourceDir, SourceDir, TargetDir) + end, + List); %% do_eval_spec({source_dir, SourceDir2, Spec}, OrigSourceDir, _SourceDir, TargetDir) -> %% %% Source dir is absolute or relative the original source dir %% SourceDir3 = filename:join([OrigSourceDir, SourceDir2]), @@ -966,12 +1177,18 @@ do_eval_spec({create_dir, Dir, Files}, OrigSourceDir, SourceDir, TargetDir) -> TargetDir2 = filename:join([TargetDir, Dir]), reltool_utils:create_dir(TargetDir2), do_eval_spec(Files, OrigSourceDir, SourceDir2, TargetDir2); -do_eval_spec({create_dir, Dir, OldDir, Files}, OrigSourceDir, _SourceDir, TargetDir) -> +do_eval_spec({create_dir, Dir, OldDir, Files}, + OrigSourceDir, + _SourceDir, + TargetDir) -> SourceDir2 = filename:join([OrigSourceDir, OldDir]), TargetDir2 = filename:join([TargetDir, Dir]), reltool_utils:create_dir(TargetDir2), do_eval_spec(Files, SourceDir2, SourceDir2, TargetDir2); -do_eval_spec({archive, Archive, Options, Files}, OrigSourceDir, SourceDir, TargetDir) -> +do_eval_spec({archive, Archive, Options, Files}, + OrigSourceDir, + SourceDir, + TargetDir) -> TmpSpec = {create_dir, "tmp", Files}, TmpDir = filename:join([TargetDir, "tmp"]), reltool_utils:create_dir(TmpDir), @@ -986,17 +1203,24 @@ do_eval_spec({archive, Archive, Options, Files}, OrigSourceDir, SourceDir, Targe {ok, _} -> ok; {error, Reason} -> - reltool_utils:throw_error("create archive ~s: ~p\n", [ArchiveFile, Reason]) + reltool_utils:throw_error("create archive ~s failed: ~p", + [ArchiveFile, Reason]) end; do_eval_spec({copy_file, File}, _OrigSourceDir, SourceDir, TargetDir) -> SourceFile = filename:join([SourceDir, File]), TargetFile = filename:join([TargetDir, File]), reltool_utils:copy_file(SourceFile, TargetFile); -do_eval_spec({copy_file, File, OldFile}, OrigSourceDir, _SourceDir, TargetDir) -> +do_eval_spec({copy_file, File, OldFile}, + OrigSourceDir, + _SourceDir, + TargetDir) -> SourceFile = filename:join([OrigSourceDir, OldFile]), TargetFile = filename:join([TargetDir, File]), reltool_utils:copy_file(SourceFile, TargetFile); -do_eval_spec({write_file, File, IoList}, _OrigSourceDir, _SourceDir, TargetDir) -> +do_eval_spec({write_file, File, IoList}, + _OrigSourceDir, + _SourceDir, + TargetDir) -> TargetFile = filename:join([TargetDir, File]), reltool_utils:write_file(TargetFile, IoList); do_eval_spec({strip_beam, File}, _OrigSourceDir, SourceDir, TargetDir) -> @@ -1007,7 +1231,7 @@ do_eval_spec({strip_beam, File}, _OrigSourceDir, SourceDir, TargetDir) -> reltool_utils:write_file(TargetFile, BeamBin2). cleanup_spec(List, TargetDir) when is_list(List) -> - lists:foreach(fun(F)-> cleanup_spec(F, TargetDir) end, List); + lists:foreach(fun(F) -> cleanup_spec(F, TargetDir) end, List); %% cleanup_spec({source_dir, _SourceDir, Spec}, TargetDir) -> %% cleanup_spec(Spec, TargetDir); cleanup_spec({create_dir, Dir, Files}, TargetDir) -> @@ -1039,9 +1263,12 @@ cleanup_spec({strip_beam, File}, TargetDir) -> filter_spec(List, InclRegexps, ExclRegexps) -> do_filter_spec("", List, InclRegexps, ExclRegexps). - + do_filter_spec(Path, List, InclRegexps, ExclRegexps) when is_list(List) -> - lists:zf(fun(File) -> do_filter_spec(Path, File, InclRegexps, ExclRegexps) end, List); + lists:zf(fun(File) -> + do_filter_spec(Path, File, InclRegexps, ExclRegexps) + end, + List); %% do_filter_spec(Path, {source_dir, _SourceDir, Spec}, InclRegexps, ExclRegexps) -> %% do_filter_spec(Path, Spec, InclRegexps, ExclRegexps); do_filter_spec(Path, {create_dir, Dir, Files}, InclRegexps, ExclRegexps) -> @@ -1057,7 +1284,10 @@ do_filter_spec(Path, {create_dir, Dir, Files}, InclRegexps, ExclRegexps) -> Files2 when is_list(Files2) -> {true, {create_dir, Dir, Files2}} end; -do_filter_spec(Path, {create_dir, NewDir, OldDir, Files}, InclRegexps, ExclRegexps) -> +do_filter_spec(Path, + {create_dir, NewDir, OldDir, Files}, + InclRegexps, + ExclRegexps) -> Path2 = opt_join(Path, NewDir), case do_filter_spec(Path2, Files, InclRegexps, ExclRegexps) of [] -> @@ -1070,7 +1300,10 @@ do_filter_spec(Path, {create_dir, NewDir, OldDir, Files}, InclRegexps, ExclRegex Files2 when is_list(Files2) -> {true, {create_dir, NewDir, OldDir, Files2}} end; -do_filter_spec(Path, {archive, Archive, Options, Files}, InclRegexps, ExclRegexps) -> +do_filter_spec(Path, + {archive, Archive, Options, Files}, + InclRegexps, + ExclRegexps) -> case do_filter_spec(Path, Files, InclRegexps, ExclRegexps) of [] -> case match(Path, InclRegexps, ExclRegexps) of @@ -1085,7 +1318,10 @@ do_filter_spec(Path, {archive, Archive, Options, Files}, InclRegexps, ExclRegexp do_filter_spec(Path, {copy_file, File}, InclRegexps, ExclRegexps) -> Path2 = opt_join(Path, File), match(Path2, InclRegexps, ExclRegexps); -do_filter_spec(Path, {copy_file, NewFile, _OldFile}, InclRegexps, ExclRegexps) -> +do_filter_spec(Path, + {copy_file, NewFile, _OldFile}, + InclRegexps, + ExclRegexps) -> Path2 = opt_join(Path, NewFile), match(Path2, InclRegexps, ExclRegexps); do_filter_spec(Path, {write_file, File, _IoList}, InclRegexps, ExclRegexps) -> @@ -1101,18 +1337,7 @@ opt_join(Path, File) -> filename:join([Path, File]). match(String, InclRegexps, ExclRegexps) -> - %%case - match(String, InclRegexps) andalso not match(String, ExclRegexps). -%% of -%% true -> -%% true; -%% false -> -%% io:format("no match: ~p\n" -%% " incl: ~p\n" -%% " excl: ~p\n", -%% [String, InclRegexps, ExclRegexps]), -%% false -%% end. + match(String, InclRegexps) andalso not match(String, ExclRegexps). %% Match at least one regexp match(_String, []) -> @@ -1131,7 +1356,7 @@ match(String, [#regexp{source = _, compiled = MP} | Regexps]) -> install(RelName, TargetDir) -> try do_install(RelName, TargetDir) - catch + catch throw:{error, Text} -> {error, Text} end. @@ -1148,7 +1373,8 @@ do_install(RelName, TargetDir) -> case os:type() of {win32, _} -> NativeRootDir = filename:nativename(TargetDir2), - %% NativeBinDir = filename:nativename(filename:join([BinDir, "win32"])), + %% NativeBinDir = + %% filename:nativename(filename:join([BinDir, "win32"])), NativeBinDir = filename:nativename(BinDir), IniData = ["[erlang]\r\n", "Bindir=", NativeBinDir, "\r\n", @@ -1157,25 +1383,30 @@ do_install(RelName, TargetDir) -> IniFile = filename:join([BinDir, "erl.ini"]), ok = file:write_file(IniFile, IniData); _ -> - subst_src_scripts(start_scripts(), ErtsBinDir, BinDir, - [{"FINAL_ROOTDIR", TargetDir2}, {"EMU", "beam"}], + subst_src_scripts(start_scripts(), + ErtsBinDir, + BinDir, + [{"FINAL_ROOTDIR", TargetDir2}, + {"EMU", "beam"}], [preserve]) end, RelFile = filename:join([RelDir, RelVsn, RelName ++ ".rel"]), ok = release_handler:create_RELEASES(TargetDir2, RelFile), ok; _ -> - reltool_utils:throw_error("~s: Illegal syntax.\n", [DataFile]) + reltool_utils:throw_error("~s: Illegal data file syntax", [DataFile]) end. subst_src_scripts(Scripts, SrcDir, DestDir, Vars, Opts) -> - Fun = fun(Script) -> subst_src_script(Script, SrcDir, DestDir, Vars, Opts) end, + Fun = fun(Script) -> + subst_src_script(Script, SrcDir, DestDir, Vars, Opts) + end, lists:foreach(Fun, Scripts). -subst_src_script(Script, SrcDir, DestDir, Vars, Opts) -> +subst_src_script(Script, SrcDir, DestDir, Vars, Opts) -> subst_file(filename:join([SrcDir, Script ++ ".src"]), filename:join([DestDir, Script]), - Vars, + Vars, Opts). subst_file(Src, Dest, Vars, Opts) -> @@ -1212,8 +1443,8 @@ subst([], _Vars, Result) -> subst_var([$%| Rest], Vars, Result, VarAcc) -> Key = lists:reverse(VarAcc), - case lists:keysearch(Key, 1, Vars) of - {value, {Key, Value}} -> + case lists:keyfind(Key, 1, Vars) of + {Key, Value} -> subst(Rest, Vars, lists:reverse(Value, Result)); false -> subst(Rest, Vars, [$% | VarAcc ++ [$% | Result]]) diff --git a/lib/reltool/src/reltool_utils.erl b/lib/reltool/src/reltool_utils.erl index 8d52ade9be..39d057d994 100644 --- a/lib/reltool/src/reltool_utils.erl +++ b/lib/reltool/src/reltool_utils.erl @@ -1,25 +1,48 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2009-2010. 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_utils). %% Public --compile([export_all]). +-export([root_dir/0, erl_libs/0, lib_dirs/1, + split_app_name/1, prim_consult/1, + default_rels/0, choose_default/3, + + assign_image_list/1, get_latest_resize/1, + 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, + 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, + + create_dir/1, list_dir/1, read_file_info/1, + write_file_info/2, read_file/1, write_file/2, + recursive_delete/1, delete/2, recursive_copy_file/2, copy_file/2, + + throw_error/2, + + decode_regexps/3, + default_val/2, + escript_foldl/3, + + call/2, cast/2, reply/3]). -include_lib("kernel/include/file.hrl"). -include_lib("wx/include/wx.hrl"). @@ -30,11 +53,11 @@ root_dir() -> erl_libs() -> case os:getenv("ERL_LIBS") of - false -> + false -> []; LibStr -> string:tokens(LibStr, ":;") - end. + end. lib_dirs(Dir) -> case erl_prim_loader:list_dir(Dir) of @@ -42,7 +65,7 @@ lib_dirs(Dir) -> [F || F <- Files, filelib:is_dir(filename:join([Dir, F]), erl_prim_loader)]; - error -> + error -> [] end. @@ -55,7 +78,7 @@ split_app_name(Name) -> Elem >= $0, Elem =< $9 -> true; true -> false end - end, + end, case lists:splitwith(Pred, lists:reverse(Name)) of {Vsn, [$- | App]} -> {list_to_atom(lists:reverse(App)), lists:reverse(Vsn)}; @@ -103,23 +126,51 @@ 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 = #rel_app{name = kernel, incl_apps = []}, + %%Stdlib = #rel_app{name = stdlib, incl_apps = []}, + Sasl = #rel_app{name = sasl, incl_apps = []}, [ #rel{name = ?DEFAULT_REL_NAME, vsn = "1.0", - rel_apps = [Kernel, Stdlib]}, + rel_apps = []}, + %%rel_apps = [Kernel, Stdlib]}, #rel{name = "start_sasl", vsn = "1.0", - rel_apps = [Kernel, Sasl, Stdlib]} + rel_apps = [Sasl]} + %%rel_apps = [Kernel, Sasl, Stdlib]} ]. +choose_default(Tag, Profile, InclDefs) + when Profile =:= ?DEFAULT_PROFILE; InclDefs -> + case Tag of + incl_sys_filters -> ?DEFAULT_INCL_SYS_FILTERS; + excl_sys_filters -> ?DEFAULT_EXCL_SYS_FILTERS; + incl_app_filters -> ?DEFAULT_INCL_APP_FILTERS; + excl_app_filters -> ?DEFAULT_EXCL_APP_FILTERS; + embedded_app_type -> ?DEFAULT_EMBEDDED_APP_TYPE + end; +choose_default(Tag, standalone, _InclDefs) -> + case Tag of + incl_sys_filters -> ?STANDALONE_INCL_SYS_FILTERS; + excl_sys_filters -> ?STANDALONE_EXCL_SYS_FILTERS; + incl_app_filters -> ?STANDALONE_INCL_APP_FILTERS; + excl_app_filters -> ?STANDALONE_EXCL_APP_FILTERS; + embedded_app_type -> ?DEFAULT_EMBEDDED_APP_TYPE + end; +choose_default(Tag, embedded, _InclDefs) -> + case Tag of + incl_sys_filters -> ?EMBEDDED_INCL_SYS_FILTERS; + excl_sys_filters -> ?EMBEDDED_EXCL_SYS_FILTERS; + incl_app_filters -> ?EMBEDDED_INCL_APP_FILTERS; + excl_app_filters -> ?EMBEDDED_EXCL_APP_FILTERS; + embedded_app_type -> ?EMBEDDED_APP_TYPE + end. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% assign_image_list(ListCtrl) -> Art = wxImageList:new(16,16), - [wxImageList:add(Art, wxArtProvider:getBitmap(Image, [{size, {16,16}}])) + [wxImageList:add(Art, wxArtProvider:getBitmap(Image, [{size, {16,16}}])) || Image <- ["wxART_ERROR", "wxART_WARNING", "wxART_QUESTION", @@ -206,7 +257,7 @@ split_app_dir(Dir) -> ParentDir = filename:dirname(Dir), Base = filename:basename(Dir), {Name, Vsn} = split_app_name(Base), - Vsn2 = + Vsn2 = try [list_to_integer(N) || N <- string:tokens(Vsn, ".")] catch @@ -276,7 +327,9 @@ get_selected_items(ListCtrl, PrevItem, Acc) -> ItemNo -> case wxListCtrl:getItemText(ListCtrl, ItemNo) of Text when Text =/= ?MISSING_APP_TEXT -> - get_selected_items(ListCtrl, ItemNo, [{ItemNo, Text} | Acc]); + get_selected_items(ListCtrl, + ItemNo, + [{ItemNo, Text} | Acc]); _Text -> get_selected_items(ListCtrl, ItemNo, Acc) end @@ -306,7 +359,8 @@ select_items(ListCtrl, OldItems, NewItems) -> select_item(ListCtrl, NewItems); ValidItems -> %% Some old selections are still valid. Select them again. - lists:foreach(fun(Item) -> select_item(ListCtrl, [Item]) end, ValidItems) + lists:foreach(fun(Item) -> select_item(ListCtrl, [Item]) end, + ValidItems) end. select_item(ListCtrl, [{ItemNo, Text} | Items]) -> @@ -339,7 +393,7 @@ 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} -> @@ -356,7 +410,7 @@ return_first_error(Status, NewError) when is_list(NewError) -> {error, OldError} -> {error, OldError} end. - + add_warning(Status, Warning) -> case Status of {ok, Warnings} -> @@ -376,7 +430,7 @@ create_dir(Dir) -> ok; {error, Reason} -> Text = file:format_error(Reason), - throw_error("create dir ~s: ~s\n", [Dir, Text]) + throw_error("create dir ~s: ~s", [Dir, Text]) end. list_dir(Dir) -> @@ -385,7 +439,7 @@ list_dir(Dir) -> Files; error -> Text = file:format_error(enoent), - throw_error("list dir ~s: ~s\n", [Dir, Text]) + throw_error("list dir ~s: ~s", [Dir, Text]) end. read_file_info(File) -> @@ -394,7 +448,7 @@ read_file_info(File) -> Info; {error, Reason} -> Text = file:format_error(Reason), - throw_error("read file info ~s: ~s\n", [File, Text]) + throw_error("read file info ~s: ~s", [File, Text]) end. write_file_info(File, Info) -> @@ -403,7 +457,7 @@ write_file_info(File, Info) -> ok; {error, Reason} -> Text = file:format_error(Reason), - throw_error("write file info ~s: ~s\n", [File, Text]) + throw_error("write file info ~s: ~s", [File, Text]) end. read_file(File) -> @@ -412,7 +466,7 @@ read_file(File) -> Bin; {error, Reason} -> Text = file:format_error(Reason), - throw_error("read file ~s: ~s\n", [File, Text]) + throw_error("read file ~s: ~s", [File, Text]) end. write_file(File, IoList) -> @@ -421,7 +475,7 @@ write_file(File, IoList) -> ok; {error, Reason} -> Text = file:format_error(Reason), - throw_error("write file ~s: ~s\n", [File, Text]) + throw_error("write file ~s: ~s", [File, Text]) end. recursive_delete(Dir) -> @@ -429,7 +483,8 @@ recursive_delete(Dir) -> true -> case file:list_dir(Dir) of {ok, Files} -> - Fun = fun(F) -> recursive_delete(filename:join([Dir, F])) end, + Fun = + fun(F) -> recursive_delete(filename:join([Dir, F])) end, lists:foreach(Fun, Files), delete(Dir, directory); {error, enoent} -> @@ -514,7 +569,9 @@ decode_regexps(Key, Regexps, _Old) when is_list(Regexps) -> do_decode_regexps(Key, [Regexp | Regexps], Acc) -> case catch re:compile(Regexp, []) of {ok, MP} -> - do_decode_regexps(Key, Regexps, [#regexp{source = Regexp, compiled = MP} | Acc]); + do_decode_regexps(Key, + Regexps, + [#regexp{source = Regexp, compiled = MP} | Acc]); _ -> Text = lists:flatten(io_lib:format("~p", [{Key, Regexp}])), throw({error, "Illegal option: " ++ Text}) @@ -532,8 +589,34 @@ default_val(Val, Default) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +escript_foldl(Fun, Acc, File) -> + case escript:extract(File, [compile_source]) of + {ok, [_Shebang, _Comment, _EmuArgs, Body]} -> + case Body of + {source, BeamCode} -> + GetInfo = fun() -> file:read_file_info(File) end, + GetBin = fun() -> BeamCode end, + {ok, Fun(".", GetInfo, GetBin, Acc)}; + {beam, BeamCode} -> + GetInfo = fun() -> file:read_file_info(File) end, + GetBin = fun() -> BeamCode end, + {ok, Fun(".", GetInfo, GetBin, Acc)}; + {archive, ArchiveBin} -> + zip:foldl(Fun, Acc, {File, ArchiveBin}) + end; + {error, Reason} -> + {error, Reason} + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + call(Name, Msg) when is_atom(Name) -> - call(whereis(Name), Msg); + case whereis(Name) of + undefined -> + {error, {noproc, Name}}; + Pid -> + call(Pid, Msg) + end; call(Pid, Msg) when is_pid(Pid) -> Ref = erlang:monitor(process, Pid), Pid ! {call, self(), Ref, Msg}, |