diff options
Diffstat (limited to 'lib/reltool')
26 files changed, 3388 insertions, 1607 deletions
diff --git a/lib/reltool/bin/reltool.escript b/lib/reltool/bin/reltool.escript new file mode 100755 index 0000000000..0dcd5ad1e9 --- /dev/null +++ b/lib/reltool/bin/reltool.escript @@ -0,0 +1,249 @@ +#!/usr/bin/env escript +%% -*- erlang -*- +%% +%% %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% + +-include_lib("reltool/src/reltool.hrl"). + +main(Args) -> + process_flag(trap_exit, true), + try + Tokens = scan_args(Args, [], []), + {Options, Actions} = parse_args(Tokens, []), + case invoke(Options, Actions) of + ok -> + safe_stop(0); + {error, ReasonString} -> + fatal_error(ReasonString, 2) + end + catch + throw:usage -> + usage(), + safe_stop(1); + exit:Reason -> + String = lists:flatten(io_lib:format("EXIT: ~p", [Reason])), + fatal_error(String, 3) + end. + +usage() -> + Usage = + [ + "[Config] [--window]", + "[Config] --create_config [-defaults] [-derived] [ConfigFile]", + "[Config] --create_rel RelName [RelFile]", + "[Config] --create_script RelName [ScriptFile]", + "[Config] --create_target TargetDir", + "[Config] --create_target_spec [SpecFile]", + "[Config] --eval_target_spec Spec TargetDir RootDir" + ], + Script = script_name(), + String = lists:flatten([[Script, " ", U, "\n"] || U <- Usage]), + io:format("Erlang/OTP release management tool\n\n" + "~s\nConfig = ConfigFile | '{sys, [sys()]}'\n" + "Spec = SpecFile | '{spec, [target_spec()}']\n\n" + "See User's guide and Reference manual for more info.\n", + [String]). + +safe_stop(Code) -> + init:stop(Code), + timer:sleep(infinity). + +invoke(Options, Actions) -> + case Actions of + [] -> + invoke(Options, [["--window"]]); + [["--window"]] -> + start_window(Options); + [["--create_config" | OptArgs]] -> + DefArg = "-defaults", + DerivArg = "-derived", + InclDef = lists:member(DefArg, OptArgs), + InclDeriv = lists:member(DerivArg, OptArgs), + case reltool:get_config(Options, InclDef, InclDeriv) of + {ok, Config} -> + String = pretty("config", Config), + case OptArgs -- [DefArg, DerivArg] of + [] -> + format("~s", [String]); + [ConfigFile] -> + write_file(ConfigFile, String); + _ -> + throw(usage) + end; + {error, Reason} -> + {error, Reason} + end; + [["--create_rel", RelName | OptArgs]] -> + case reltool:get_rel(Options, RelName) of + {ok, Rel} -> + String = pretty("rel", Rel), + case OptArgs of + [] -> + format("~s", [String]); + [RelFile] -> + write_file(RelFile, String); + _ -> + throw(usage) + end; + {error, Reason} -> + {error, Reason} + end; + [["--create_script", RelName | OptArgs]] -> + case reltool:get_script(Options, RelName) of + {ok, Script} -> + String = pretty("script", Script), + case OptArgs of + [] -> + format("~s", [String]); + [ScriptFile] -> + write_file(ScriptFile, String); + _ -> + throw(usage) + end; + {error, Reason} -> + {error, Reason} + end; + [["--create_target", TargetDir]] -> + reltool:create_target(Options, TargetDir); + [["--create_target_spec" | OptArgs]] -> + case reltool:get_target_spec(Options) of + {ok, Script} -> + String = pretty("target_spec", Script), + case OptArgs of + [] -> + format("~s", [String]); + [SpecFile] -> + write_file(SpecFile, String); + _ -> + throw(usage) + end; + {error, Reason} -> + {error, Reason} + end; + [["--eval_target_spec", TargetSpec, TargetDir, RootDir]] -> + try + {ok, Tokens, _} = erl_scan:string(TargetSpec ++ ". "), + {ok, {spec, Spec}} = erl_parse:parse_term(Tokens), + reltool:eval_target_spec(Spec, TargetDir, RootDir) + catch + error:{badmatch, _} -> + case file:consult(TargetSpec) of + {ok, Spec2} -> + reltool:eval_target_spec(Spec2, TargetDir, RootDir); + {error, Reason} -> + Text = file:format_error(Reason), + {error, TargetSpec ++ ": " ++ Text} + end + end; + _ -> + throw(usage) + end. + +start_window(Options) -> + case reltool:start_link(Options) of + {ok, WinPid} -> + receive + {'EXIT', WinPid, shutdown} -> + ok; + {'EXIT', WinPid, normal} -> + ok; + {'EXIT', WinPid, Reason} -> + exit(Reason) + end; + {error, Reason} -> + {error, Reason} + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Helpers +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +script_name() -> + filename:basename(escript:script_name(), ".escript"). + +fatal_error(String, Code) -> + io:format(standard_error, "~s: ~s\n", [script_name(), String]), + safe_stop(Code). + +write_file(File, IoList) -> + case file:write_file(File, IoList) of + ok -> + ok; + {error, Reason} -> + {error, file:format_error(Reason)} + end. + +format(Format, Args) -> + io:format(Format, Args), + %% Wait a while for the I/O to be processed + timer:sleep(timer:seconds(1)). + +pretty(Tag, Term) -> + lists:flatten(io_lib:format("%% ~s generated at ~w ~w\n~p.\n\n", + [Tag, date(), time(), Term])). + +scan_args([H | T], Single, Multi) -> + case H of + "--" ++ _ when Single =:= [] -> + scan_args(T, [H], Multi); + "--" ++ _ -> + scan_args(T, [H], [lists:reverse(Single) | Multi]); + _ -> + scan_args(T, [H | Single], Multi) + end; +scan_args([], [], Multi) -> + lists:reverse(Multi); +scan_args([], Single, Multi) -> + lists:reverse([lists:reverse(Single) | Multi]). + +parse_args([H | T] = Args, Options) -> + case H of + ["--wx_debug" | Levels] -> + Dbg = + fun(L) -> + case catch list_to_integer(L) of + {'EXIT', _} -> + case catch list_to_atom(L) of + {'EXIT', _} -> + exit("Illegal wx debug level: " ++ L); + Atom -> + Atom + end; + Int -> + Int + end + end, + Levels2 = lists:map(Dbg, Levels), + parse_args(T, [{wx_debug, Levels2} | Options]); + ["--" ++ _ | _] -> + %% No more options + {lists:reverse(Options), Args}; + [Config] -> + try + {ok, Tokens, _} = erl_scan:string(Config ++ ". "), + {ok, {sys, _} = Sys} = erl_parse:parse_term(Tokens), + parse_args(T, [{config, Sys} | Options]) + catch + error:{badmatch, _} -> + parse_args(T, [{config, Config} | Options]); + X:Y -> + io:format("\n\n~p\n\n", [{X, Y}]) + end + end; +parse_args([], Options) -> + {lists:reverse(Options), []}. diff --git a/lib/reltool/doc/src/notes.xml b/lib/reltool/doc/src/notes.xml index 524d728901..95e379db53 100644 --- a/lib/reltool/doc/src/notes.xml +++ b/lib/reltool/doc/src/notes.xml @@ -37,6 +37,73 @@ thus constitutes one section in this document. The title of each section is the version number of Reltool.</p> + <section><title>Reltool 0.5.4</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Added function <c>zip:foldl/3</c> to iterate over zip + archives.</p> + <p> + Added functions to create and extract escripts. See + <c>escript:create/2</c> and <c>escript:extract/2</c>.</p> + <p> + The undocumented function <c>escript:foldl/3</c> has been + removed. The same functionality can be achieved with the + more flexible functions <c>escript:extract/2</c> and + <c>zip:foldl/3</c>.</p> + <p> + Record fields has been annotated with type info. Source + files as been adapted to fit within 80 chars and trailing + whitespace has been removed.</p> + <p> + Own Id: OTP-8521</p> + </item> + <item> + <p>A new escript, called <c>reltool</c>, has been + introduced in order to simplify the usage of the reltool + application from makefiles.</p> + <p>The handling of applications included in releases has + been improved. Applications that are required to be + started before other applications in a release are now + automatically included in the release. The <c>kernel</c> + and <c>stdlib</c> applications are always included as + they are mandatory.</p> + <p>Applications that are (explicitly or implicitly) + included in a release are now automatically included as + if they were explicitly included with the incl_cond + flag.</p> + <p>A new <c>embedded_app_type</c> option has been + introduced. It is intended to be used for embedded + systems where all included applications must be loaded + from the boot script, as these systems does not utilize + dynamic code loading. If <c>embedded_app_type </c> is set + to something else than <c>undefined</c>, all included + applications will be included in both the release as well + as in the boot script. If the <c>profile</c> is + <c>embedded</c> the <c>embedded_app_type</c> option + defaults to <c>load</c>.</p> + <p>A new function called <c>reltool:get_status/1</c> has + been introduced. It returns status about the + configuration in the server.</p> + <p>The API functions that may take <c>PidOrOptions</c> as + input and actually gets <c>Options</c> does now print out + warnings.</p> + <p>The internal error handling has been improved. For + example <c>{error,Reason}</c> is always returned in case + of errors even when the server dies.</p> + <p><c>app</c> and <c>appup</c> files has been added as + well as a corresponding test suite.</p> + <p>Various cleanups has been made in the code and in the + documentation.</p> + <p> + Own Id: OTP-8590</p> + </item> + </list> + </section> + + </section> <section><title>Reltool 0.5.3</title> diff --git a/lib/reltool/doc/src/reltool.xml b/lib/reltool/doc/src/reltool.xml index 9786928ae8..598594145a 100644 --- a/lib/reltool/doc/src/reltool.xml +++ b/lib/reltool/doc/src/reltool.xml @@ -48,8 +48,8 @@ <c>root_dir</c> is the root directory of the analysed system and it defaults to the system executing <c>reltool</c>. Applications may also be located outside <c>root_dir</c>. <c>lib_dirs</c> - defines additional library directories where applications - additional may reside and it defaults to the the directories + defines library directories where additional applications + may reside and it defaults to the directories listed by the operating system environment variable <c>ERL_LIBS</c>. See the module <c>code</c> for more info. Finally single modules and entire applications may be read from @@ -58,23 +58,23 @@ <p>Some configuration parameters control the behavior of Reltool on system (<c>sys</c>) level. Others provide control on application (<c>app</c>) level and yet others are on module - (<c>mod</c>) level. Module level parameters overrides application - level parameters and application level parameters overrides system + (<c>mod</c>) level. Module level parameters override application + level parameters and application level parameters override system level parameters. Escript <c>escript</c> level parameters - overrides system level parameters.</p> + override system level parameters.</p> <p>The following top level <c>options</c> are supported:</p> <taglist> - <tag><c><![CDATA[config]]></c></tag> + <tag><c>config</c></tag> <item> <p>This is the main option and it controls the configuration of <c>reltool</c>. It can either be a <c>sys</c> tuple or a name of a <c>file</c> containing a sys tuple.</p> </item> - <tag><c><![CDATA[trap_exit]]></c></tag> + <tag><c>trap_exit</c></tag> <item> <p>This option controls the error handling behavior of <c>reltool</c>. By default the window processes traps @@ -82,7 +82,7 @@ <c>trap_exit</c> to <c>false</c>.</p> </item> - <tag><c><![CDATA[wx_debug]]></c></tag> + <tag><c>wx_debug</c></tag> <item> <p>This option controls the debug level of <c>wx</c>. As its name indicates it is only useful for debugging. See @@ -97,36 +97,36 @@ <taglist> - <tag><c><![CDATA[erts]]></c></tag> + <tag><c>erts</c></tag> <item> <p>Erts specific configuration. See application level options below.</p> </item> - <tag><c><![CDATA[escript]]></c></tag> + <tag><c>escript</c></tag> <item> <p>Escript specific configuration. An escript has a mandatory file name and escript level options that are described below.</p> </item> - <tag><c><![CDATA[app]]></c></tag> + <tag><c>app</c></tag> <item> <p>Application specific configuration. An application has a mandatory name and application level options that are described below.</p> </item> - <tag><c><![CDATA[mod_cond]]></c></tag> + <tag><c>mod_cond</c></tag> <item> <p>This parameter controls the module inclusion policy. It defaults to <c>all</c> which means that if an application is included (either explicitly or implicitly) all modules in that application will be included. This implies that both modules - that exists on the <c>ebin</c> directory of the application, + that exist in the <c>ebin</c> directory of the application, as well as modules that are named in the <c>app</c> file will be included. If the parameter is set to <c>ebin</c>, both - modules on the <c>ebin</c> directory and derived modules are + modules in the <c>ebin</c> directory and derived modules are included. If the parameter is set to <c>app</c>, both modules in the <c>app</c> file and derived modules are included. <c>derived</c> means that only modules that are used by other @@ -134,69 +134,69 @@ system level is used as default for all applications.</p> </item> - <tag><c><![CDATA[incl_cond]]></c></tag> + <tag><c>incl_cond</c></tag> <item> - <p>This parameter controls the application and escript - inclusion policy. It defaults to <c>derived</c> which means - that the applications that not have any explicit - <c>incl_cond</c> setting, will only be included if any other - (explicitly or implicitly included) application uses it. The - value <c>include</c> implies that all applications and - escripts that that not have any explicit <c>incl_cond</c> - setting will be included. <c>exclude</c> implies that all - applications and escripts) that that not have any explicit - <c>incl_cond</c> setting will be excluded.</p> + <p>This parameter controls the application and escript + inclusion policy. It defaults to <c>derived</c> which means + that the applications that do not have any explicit + <c>incl_cond</c> setting, will only be included if any other + (explicitly or implicitly included) application uses it. The + value <c>include</c> implies that all applications and + escripts that do not have any explicit <c>incl_cond</c> + setting will be included. <c>exclude</c> implies that all + applications and escripts) that do not have any explicit + <c>incl_cond</c> setting will be excluded.</p> </item> - <tag><c><![CDATA[boot_rel]]></c></tag> + <tag><c>boot_rel</c></tag> <item> <p>A target system may have several releases but the one given as <c>boot_rel</c> will be used as default when the system is booting up.</p> </item> - <tag><c><![CDATA[rel]]></c></tag> + <tag><c>rel</c></tag> <item> <p>Release specific configuration. Each release maps to a - <c>rel</c>, <c>script</c> and <c>boot </c> file. See the + <c>rel</c>, <c>script</c> and <c>boot</c> file. See the module <c>systools</c> for more info about the details. Each release has a name, a version and a set of applications with a few release specific parameters such as type and included applications.</p> </item> - <tag><c><![CDATA[relocatable]]></c></tag> + <tag><c>relocatable</c></tag> <item> - <p>This parameter controls whether the <c>erl</c> executable - in the target system automatically should determine where it - is installed or if it should use a hardcoded path to the - installation. In the latter case the target system must be - installed with <c>reltool:install/2</c> before it can be - used. If the system is relocatable, the file tree containing - the target system can be moved to another location without - re-installation. The default is <c>true</c>.</p> + <p>This parameter controls whether the <c>erl</c> executable + in the target system should automatically determine where it + is installed or if it should use a hardcoded path to the + installation. In the latter case the target system must be + installed with <c>reltool:install/2</c> before it can be + used. If the system is relocatable, the file tree containing + the target system can be moved to another location without + re-installation. The default is <c>true</c>.</p> </item> - <tag><c><![CDATA[profile]]></c></tag> + <tag><c>profile</c></tag> <item> - <p>The creation of the specification for a target system is - performed in two steps. In the first step a complete - specification is generated. It will likely contain much more - files than you are interested in your customized target - system. In the second step the specification will be filtered - according to your filters. There you have the ability to - specify filters per application as well as system wide - filters. You can also select a <c>profile</c> for your - system. Depending on the <c>profile</c>, different default - filters will be used. There are three different profiles to - choose from: <c>development</c>, <c>embedded</c> and - <c>standalone</c>. <c>development</c> is default. The - parameters that are affected by the <c>profile</c> are: - <c>incl_sys_filters</c>, <c>excl_sys_filters</c>, - <c>incl_app_filters</c> and <c>excl_app_filters</c>.</p> + <p>The creation of the specification for a target system is + performed in two steps. In the first step a complete + specification is generated. It will likely contain much more + files than you are interested in in your customized target + system. In the second step the specification will be filtered + according to your filters. There you have the ability to + specify filters per application as well as system wide + filters. You can also select a <c>profile</c> for your + system. Depending on the <c>profile</c>, different default + filters will be used. There are three different profiles to + choose from: <c>development</c>, <c>embedded</c> and + <c>standalone</c>. <c>development</c> is default. The + parameters that are affected by the <c>profile</c> are: + <c>incl_sys_filters</c>, <c>excl_sys_filters</c>, + <c>incl_app_filters</c> and <c>excl_app_filters</c>.</p> </item> - <tag><c><![CDATA[app_file]]></c></tag> + <tag><c>app_file</c></tag> <item> <p>This parameter controls the default handling of the <c>app</c> files when a target system is generated. It @@ -213,7 +213,7 @@ and <c>strip</c>.</p> </item> - <tag><c><![CDATA[debug_info]]></c></tag> + <tag><c>debug_info</c></tag> <item> <p>The <c>debug_info</c> parameter controls whether the debug information in the beam file should be kept (<c>keep</c>) or @@ -221,31 +221,31 @@ system.</p> </item> - <tag><c><![CDATA[incl_sys_filters]]></c></tag> + <tag><c>incl_sys_filters</c></tag> <item> <p>This parameter normally contains a list of regular - expressions that controls which files in the system that + expressions that controls which files in the system should be included. Each file in the target system must match at least one of the listed regular expressions in order to be included. Further the files may not match any filter in <c>excl_sys_filters</c> in order to be included. Which - application files that should be included are controlled with + application files should be included is controlled with the parameters <c>incl_app_filters</c> and <c>excl_app_filters</c>. This parameter defaults to <c>[".*"]</c>.</p> </item> - <tag><c><![CDATA[excl_sys_filters]]></c></tag> + <tag><c>excl_sys_filters</c></tag> <item> <p>This parameter normally contains a list of regular - expressions that controls which files in the system that not - should be included in the target system. In order to be + expressions that controls which files in the system should + not be included in the target system. In order to be included, a file must match some filter in <c>incl_sys_filters</c> but not any filter in <c>excl_sys_filters</c>. This parameter defaults to <c>[]</c>.</p> </item> - <tag><c><![CDATA[incl_app_filters]]></c></tag> + <tag><c>incl_app_filters</c></tag> <item> <p>This parameter normally contains a list of regular expressions that controls which application specific files @@ -256,23 +256,23 @@ parameter defaults to <c>[".*"]</c>.</p> </item> - <tag><c><![CDATA[excl_app_filters]]></c></tag> + <tag><c>excl_app_filters</c></tag> <item> <p>This parameter normally contains a list of regular expressions that controls which application specific files - that not should be included in the target system. In order to + should not be included in the target system. In order to be included, a file must match some filter in <c>incl_app_filters</c> but not any filter in <c>excl_app_filters</c>. This parameter defaults to <c>[]</c>.</p> </item> - <tag><c><![CDATA[incl_archive_filters]]></c></tag> + <tag><c>incl_archive_filters</c></tag> <item> <p>This parameter normally contains a list of regular expressions that controls which top level directories in an - application that should be included in an archive file (as - opposed of beeing included as a regular directory outside the + application should be included in an archive file (as + opposed to being included as a regular directory outside the archive). Each top directory in the application must match at least one of the listed regular expressions in order to be included. Further the files may not match any filter in @@ -280,122 +280,122 @@ parameter defaults to <c>[".*"]</c>.</p> </item> - <tag><c><![CDATA[excl_archive_filters]]></c></tag> + <tag><c>excl_archive_filters</c></tag> <item> <p>This parameter normally contains a list of regular expressions that controls which top level directories in an - application that not should be included in an archive file. In + application should not be included in an archive file. In order to be included in the application archive, a top directory must match some filter in <c>incl_archive_filters</c> but not any filter in <c>excl_archive_filters</c>. This parameter defaults to <c>["^include$","^priv$"]</c>.</p> </item> - <tag><c><![CDATA[archive_opts]]></c></tag> + <tag><c>archive_opts</c></tag> <item> <p>This parameter contains a list of options that are given to <c>zip:create/3</c> when application specific files are - packaged into an archive. All options are not supported. The - most useful options in this context, are the ones that - controls which types of files that should be compressed. This + packaged into an archive. Only a subset of the options are + supported. The most useful options in this context are the ones + that control which types of files should be compressed. This parameter defaults to <c>[]</c>.</p> </item> </taglist> - <p>On application (<c>escript</c>) level,the following options are + <p>On application (<c>escript</c>) level, the following options are supported:</p> <taglist> - <tag><c><![CDATA[incl_cond]]></c></tag> + <tag><c>incl_cond</c></tag> <item> <p>The value of this parameter overrides the parameter with the same name on system level.</p> </item> </taglist> - <p>On application (<c>app</c>) level,the following options are + <p>On application (<c>app</c>) level, the following options are supported:</p> <taglist> - <tag><c><![CDATA[vsn]]></c></tag> + <tag><c>vsn</c></tag> <item> <p>The version of the application. In an installed system there may exist several versions of an application. The <c>vsn</c> parameter - controls which version of the application that will be choosen. If it - is omitted, the latest version will be choosen.</p> + controls which version of the application will be chosen. If it + is omitted, the latest version will be chosen.</p> </item> - <tag><c><![CDATA[mod]]></c></tag> + <tag><c>mod</c></tag> <item> <p>Module specific configuration. A module has a mandatory name and module level options that are described below.</p> </item> - <tag><c><![CDATA[mod_cond]]></c></tag> + <tag><c>mod_cond</c></tag> <item> <p>The value of this parameter overrides the parameter with the same name on system level.</p> </item> - <tag><c><![CDATA[incl_cond]]></c></tag> + <tag><c>incl_cond</c></tag> <item> <p>The value of this parameter overrides the parameter with the same name on system level.</p> </item> - <tag><c><![CDATA[app_file]]></c></tag> + <tag><c>app_file</c></tag> <item> <p>The value of this parameter overrides the parameter with the same name on system level.</p> </item> - <tag><c><![CDATA[debug_info]]></c></tag> + <tag><c>debug_info</c></tag> <item> <p>The value of this parameter overrides the parameter with the same name on system level.</p> </item> - <tag><c><![CDATA[incl_app_filters]]></c></tag> + <tag><c>incl_app_filters</c></tag> <item> <p>The value of this parameter overrides the parameter with the same name on system level.</p> </item> - <tag><c><![CDATA[excl_app_filters]]></c></tag> + <tag><c>excl_app_filters</c></tag> <item> <p>The value of this parameter overrides the parameter with the same name on system level.</p> </item> - <tag><c><![CDATA[incl_archive_filters]]></c></tag> + <tag><c>incl_archive_filters</c></tag> <item> <p>The value of this parameter overrides the parameter with the same name on system level.</p> </item> - <tag><c><![CDATA[excl_archive_filters]]></c></tag> + <tag><c>excl_archive_filters</c></tag> <item> <p>The value of this parameter overrides the parameter with the same name on system level.</p> </item> - <tag><c><![CDATA[archive_opts]]></c></tag> + <tag><c>archive_opts</c></tag> <item> <p>The value of this parameter overrides the parameter with the same name on system level.</p> </item> </taglist> - <p>On module (<c>mod</c>) level,the following options are + <p>On module (<c>mod</c>) level, the following options are supported:</p> <taglist> - <tag><c><![CDATA[incl_cond]]></c></tag> + <tag><c>incl_cond</c></tag> <item> - <p>This parameter controls whether the module is included or not. By - default the <c>mod_incl</c> parameter on application and system level - will be used to control whether the module is included or not. The - value of <c>incl_cond</c> overrides the module inclusion policy. - <c>include</c> implies that the module is included, while - <c>exclude</c> implies that the module not is included. - <c>derived</c> implies that the is included if any included uses the - module.</p> + <p>This parameter controls whether the module is included or not. By + default the <c>mod_incl</c> parameter on application and system level + will be used to control whether the module is included or not. The + value of <c>incl_cond</c> overrides the module inclusion policy. + <c>include</c> implies that the module is included, while + <c>exclude</c> implies that the module is not included. + <c>derived</c> implies that the module is included if it is used + by any other included module.</p> </item> - <tag><c><![CDATA[debug_info]]></c></tag> + <tag><c>debug_info</c></tag> <item> <p>The value of this parameter overrides the parameter with the same name on application level.</p> @@ -477,7 +477,9 @@ mod_name() = atom() profile() = development | embedded | standalone re_regexp() = string() reason() = string() -regexps() = [re_regexp()] | {add, [re_regexp()]} | {del, [re_regexp()]} +regexps() = [re_regexp()] + | {add, [re_regexp()]} + | {del, [re_regexp()]} rel_file() = term() rel_name() = string() rel_vsn() = string() @@ -487,7 +489,19 @@ script_file() = term() server() = server_pid() | options() server_pid() = pid() target_dir() = file() -window_pid() = pid()]]></code> +window_pid() = pid() +base_dir() = dir() +base_file() = file() +top_dir() = file() +top_file() = file() +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()}]]></code> <marker id="start"></marker> </section> @@ -497,9 +511,9 @@ window_pid() = pid()]]></code> <name>create_target(Server, TargetDir) -> ok | {error, Reason}</name> <fsummary>Create a target system</fsummary> <type> - <v>Server = server()</v> + <v>Server = server()</v> <v>TargetDir = target_dir()</v> - <v>Reason = reason()</v> + <v>Reason = reason()</v> </type> <desc><p>Create a target system. Gives the same result as <c>{ok,TargetSpec}=reltool:get_target_spec(Server)</c> and @@ -532,12 +546,12 @@ window_pid() = pid()]]></code> files are by default copied to the target system. The <c>releases</c> directory contains generated <c>rel</c>, <c>script</c>, and <c>boot</c> files. The <c>lib</c> directory - contains the applications. Which applications that are included + contains the applications. Which applications are included and if they should be customized (archived, stripped from debug info etc.) is specified with various configuration parameters. The files in the <c>bin</c> directory are copied from the <c>erts-vsn/bin</c> directory, but only those files - that was originally included in <c>bin</c> directory of the + that were originally included in the <c>bin</c> directory of the source system.</p> <p>If the configuration parameter <c>relocatable</c> was set to @@ -570,10 +584,10 @@ window_pid() = pid()]]></code> <v>Reason = reason()</v> </type> <desc><p>Get reltool configuration. Normally, only the explicit - configuration parameters with values that differs from their + configuration parameters with values that differ from their defaults are interesting. But the builtin default values can be returned by setting <c>InclDefaults</c> to <c>true</c>. The - derived configuration can be return by setting + derived configuration can be returned by setting <c>InclDerived</c> to <c>true</c>.</p></desc> </func> @@ -604,6 +618,17 @@ window_pid() = pid()]]></code> </func> <func> + <name>get_status(Server) -> {ok, [Warning]} | {error, Reason}</name> + <fsummary>Get contents of a release file</fsummary> + <type> + <v>Server = server()</v> + <v>Warning = string()</v> + <v>Reason = reason()</v> + </type> + <desc><p>Get status about the configuration</p></desc> + </func> + + <func> <name>get_server(WindowPid) -> {ok, ServerPid} | {error, Reason}</name> <fsummary>Start server process with options</fsummary> <type> @@ -680,7 +705,7 @@ window_pid() = pid()]]></code> <v>Reason = reason()</v> </type> <desc><p>Start a server process with options. The server process - identity can be given as argument to several other functions in the + identity can be given as an argument to several other functions in the API.</p></desc> </func> diff --git a/lib/reltool/doc/src/reltool_examples.xml b/lib/reltool/doc/src/reltool_examples.xml index d6db246f6c..bce9413b52 100644 --- a/lib/reltool/doc/src/reltool_examples.xml +++ b/lib/reltool/doc/src/reltool_examples.xml @@ -249,11 +249,11 @@ Eshell V5.7.3 (abort with ^G) <pre> 5> {ok, Server} = reltool:start_server([{config, {sys, [{boot_rel, "NAME"}, {rel, "NAME", "VSN", - [kernel, stdlib, sasl]}]}}]). + [sasl]}]}}]). {ok,<0.1288.0>} 6> reltool:get_config(Server). {ok,{sys,[{boot_rel,"NAME"}, - {rel,"NAME","VSN",[kernel,stdlib,sasl]}]}} + {rel,"NAME","VSN",[sasl]}]}} 7> reltool:get_rel(Server, "NAME"). {ok,{release,{"NAME","VSN"}, {erts,"5.7"}, diff --git a/lib/reltool/doc/src/reltool_usage.xml b/lib/reltool/doc/src/reltool_usage.xml index 885828d1f0..0a053a014e 100644 --- a/lib/reltool/doc/src/reltool_usage.xml +++ b/lib/reltool/doc/src/reltool_usage.xml @@ -44,7 +44,7 @@ <section> <title>System window</title> <p>The system window is started with the function - <c>reltool:start/1</c>. At startup the tool will process the all + <c>reltool:start/1</c>. At startup the tool will process all <c>beam</c> files and <c>app</c> files in order to find out dependencies between applications and their modules. Once all this information has been derived, it will be possible to explore the tool.</p> @@ -67,29 +67,29 @@ <section> <title>Libraries</title> <p>On the library page it is possible to control which sources - that the tool will use. The page is organized as a tree which + the tool will use. The page is organized as a tree which can be expanded and collapsed by clicking on the little symbol in the beginning of the expandable/collapsible lines.</p> <p>The <c>Root directory</c> can be edited by selecting the line where the path of the root directory is displayed and - clicking with the right mouse button. Choose edit in the menu - that pops up. </p> + clicking the right mouse button. Choose edit in the menu + that pops up.</p> <p>Library directories can be added, edited or deleted. This is done by selecting the line where the path to a library - directory is displayed and clicking with the right mouse + directory is displayed and clicking the right mouse button. Choose add, edit or delete in the menu that pops up. New library directories can also be added by selecting the - line <c>Library directories</c> and clicking with the right + line <c>Library directories</c> and clicking the right mouse button. Choose add in the menu that pops up.</p> <p>Escript files can be added, edited or deleted. This is done by selecting the line where the path to an escript file is - displayed and clicking with the right mouse button. Choose + displayed and clicking the right mouse button. Choose add, edit or delete in the menu that pops up. New escripts can also be added by selecting the line <c>Escript files</c> and - clicking with the right mouse button. Choose add in the menu + clicking the right mouse button. Choose add in the menu that pops up.</p> <p>When libraries and escripts are expanded, the names of @@ -102,7 +102,7 @@ <p>On the system settings page it is possible to control some global settings that are used as defaults for all applications. Set the <c>Application inclusion policy</c> to - <c>include</c> to include all applications that not are + <c>include</c> to include all applications that are not explicitly excluded. See <c>incl_cond</c> (application inclusion) and <c>mod_cond</c> (module inclusion) in the reference manual for the module <c>reltool</c> for more @@ -131,14 +131,14 @@ <p>The symbols in front of the application names are intended to describe the status of the application. There are error - symbols and warning symbols that means that there are - something that needs attention. The tick symbol means that the + and warning symbols to signalize that there is + something which needs attention. The tick symbol means that the application is included or derived and no problem has been detected. The cross symbol means that the application is excluded or available and no problem has been detected. Applications with error symbols are listed first in - each category, then comes the warnings and the normal ones - (ticks and crosses) are found at the end.</p> + each category and are followed by the warnings and the + normal ones (ticks and crosses) at the end.</p> <p>Double click on an application to launch its application window.</p> @@ -175,8 +175,8 @@ </item> <item> <p><c>Save configuration</c> - Saves the current - configuration to file. Normally, only the explictit - configuration parameters with values that differs from their + configuration to file. Normally, only the explicit + configuration parameters with values that differ from their defaults are saved. But the configuration with or without default values and with or without derived values may also be saved.</p> @@ -204,21 +204,21 @@ <p>It is possible to perform some limited manipulations of the graph. Nodes can be moved, selected, locked or deleted. Move a single node or the entire graph by moving the mouse while the - left mouse button is pressed. A node is can be locked into a fix + left mouse button is pressed. A node can be locked into a fix position by holding down the shift button when the left mouse button is released. Select several nodes by moving the mouse - while the control key and the left mouse button i + while the control key and the left mouse button are pressed. Selected nodes can be locked, unlocked or deleted by - klicking on a suitable button.</p> + clicking on a suitable button.</p> <p>The algorithm that is used to draw a graph with as few crossed links as possible is called force graph. A force graph - consists of nodes and directed link between nodes. Each node is + consists of nodes and directed links between nodes. Each node is associated with a repulsive force that pushes nodes away from each other. This force can be adjusted with the left slider or with the mouse wheel. Each link is associated with an attractive - force that pulls the nodes nearer each other. This force can be - adjusted with the right slider. If this force becomes to strong, + force that pulls the nodes nearer to each other. This force can be + adjusted with the right slider. If this force becomes too strong, the graph will be unstable. The third parameter that can be adjusted is the length of the links. It is adjusted with the middle slider.</p> @@ -256,7 +256,7 @@ <p>Select version of the application in the <c>Source selection policy</c> part of the page. By default the latest version of the application is selected, but it is possible to override this by - explicitly select another version.</p> + explicitly selecting another version.</p> <p>By default the <c>Application inclusion policy</c> on system level is used for all applications. Set the value to @@ -272,10 +272,10 @@ want actually used modules to be included. Set it to <c>app</c> if you, besides derived modules, also want the modules listed in the app file to be included. Set it to <c>ebin</c> if you, besides - derived modules, also want the modules that exists as beam files - on the ebin directory to be included. Set it to <c>all</c> if you + derived modules, also want the modules that exist as beam files + in the ebin directory to be included. Set it to <c>all</c> if you want all modules to be included, that is the union of modules - found on the ebin directory and listed in the app file.</p> + found in the ebin directory and listed in the app file.</p> <p>The application settings page is rather incomplete.</p> </section> @@ -299,16 +299,16 @@ undone.</p> <p>The symbols in front of the module names are intended to - describe the status of the module. There are error symbols - and warning symbols that means that there are something that needs + describe the status of the module. There are error and + and warning symbols to signalize that there is something that needs attention. The tick symbol means that the module is included or derived and no problem has been detected. The cross symbol means that the module is excluded or available and no problem has been detected. Modules with error symbols are listed - first in each category, then comes the warnings and the normal - ones (ticks and crosses) are found at the end.</p> + first in each category and are followed by warnings and the + normal ones (ticks and crosses) at the end.</p> - <p>Double click on an module to launch its module window.</p> + <p>Double click on a module to launch its module window.</p> </section> @@ -323,7 +323,7 @@ <c>app</c> file. If the application includes other applications, these are listed under <c>Included</c>. These applications are listed in the <c>included_applications</c> part of the <c>app</c> - file. If the application uses modules other applications, these + file. If the application uses other applications, these are listed under <c>Uses</c>.</p> <p>Double click on an application name to launch an application window.</p> @@ -336,7 +336,7 @@ <p>There are two categories of modules on the <c>Module dependencies</c> page. If the module is used by other modules, these are listed under <c>Modules used by others</c>. If the - module uses modules other modules, these are listed under <c>Used + module uses other modules, these are listed under <c>Used modules</c>.</p> <p>Double click on an module name to launch a module window.</p> @@ -365,8 +365,8 @@ <p>There are two categories of modules on the <c>Dependencies</c> page. If the module is used by other modules, these are listed - under <c>Modules used by others</c>. If the module uses modules - other modules, these are listed under <c>Used modules</c>.</p> + under <c>Modules used by others</c>. If the module uses other + modules, these are listed under <c>Used modules</c>.</p> <p>Double click on an module name to launch a module window.</p> @@ -378,7 +378,7 @@ <p>On the <c>Code</c> page the Erlang source code is displayed. It is possible to search forwards and backwards for text in the module. Enter a regular expression in the <c>Find</c> field and - press enter. It is also possible to goto a certain line on the + press enter. It is also possible to go to a certain line in the module. The <c>Back</c> button can be used to go back to the previous position.</p> 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..b80753e8fc 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-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% %% +%% %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_app_win, + reltool, + 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..9dd0a24f46 100644 --- a/lib/reltool/src/reltool.erl +++ b/lib/reltool/src/reltool.erl @@ -1,160 +1,56 @@ %% %% %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). %% 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()}}. +-spec start(options()) -> {ok, window_pid()} | {error, reason()}. start(Options)when is_list(Options) -> - {ok, WinPid} = start_link(Options), - unlink(WinPid), - {ok, WinPid}. + case start_link(Options) of + {ok, WinPid} -> + unlink(WinPid), + {ok, WinPid}; + Other-> + Other + 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()}}. @@ -164,7 +60,7 @@ start_link(Options) when is_list(Options) -> {ok, WinPid}; {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()}. @@ -200,51 +96,96 @@ 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()) -> term())) -> + {ok, server_pid()} | {error, reason()}. +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, Reason} + 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} -> + stop(Pid), + {error, Reason} 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..1a34ced89d 100644 --- a/lib/reltool/src/reltool.hrl +++ b/lib/reltool/src/reltool.hrl @@ -1,152 +1,255 @@ %% %% %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% --define(APPLICATION, reltool). --define(MISSING_APP, '*MISSING*'). +-define(APPLICATION, reltool). +-define(MISSING_APP_NAME, '*MISSING*'). -define(MISSING_APP_TEXT, "*MISSING*"). +-type file() :: string(). +-type dir() :: string(). +%% 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(). + +-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 ets_tab() :: term(). +-type status() :: missing | ok. + -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() + sys_debug :: term(), + wx_debug :: term(), + trap_exit :: boolean(), + app_tab :: ets_tab(), + mod_tab :: ets_tab(), + mod_used_by_tab :: ets_tab() }). --record(sys, - { - %% Sources - root_dir, % directory() - lib_dirs, % [directory()] - escripts, % [file()] - mod_cond, % all | app | ebin | derived | none - incl_cond, % include | exclude | derived - apps, % [#app{}] - %% 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(), --record(rel, - { - name, % string() - vsn, % string() - rel_apps % [#rel_app{}] + %% Dynamic + status :: status(), + used_by_mods :: [mod_name()], + is_pre_included :: boolean() | undefined, + is_included :: boolean() | undefined }). --record(rel_app, +-record(app_info, { - name, % atom() - app_type, % permanent | transient | temporary | load | none - incl_apps % [atom()] + 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(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{}] + 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 :: 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()], %% 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(), + 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 :: 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()], + 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,12 +268,13 @@ -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_EXCL_ARCHIVE_FILTERS, ["^include\$", "^priv\$"]). -define(DEFAULT_ARCHIVE_OPTS, []). -define(DEFAULT_INCL_SYS_FILTERS, [".*"]). @@ -178,25 +282,28 @@ -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", +-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_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..281d2c8ad4 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-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_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) -> @@ -690,19 +749,26 @@ create_editor(Parent) -> %% 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..039ad56aa8 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-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_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:keysearch(AppName, #app.name, Apps) of + {value, #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 = + Status = case lists:keysearch(missing, #mod.status, Mods2) of {value, _} -> 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,17 +813,16 @@ 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} = case A#app.use_selected_vsn of - undefined -> + undefined -> {shrinked, []}; false -> {shrinked, []}; @@ -715,8 +843,7 @@ filter_app(A) -> label = undefined, info = undefined, mods = Mods, - uses_mods = undefined, - is_included = undefined}} + uses_mods = undefined}} end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -730,37 +857,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 +905,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 +925,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 +988,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 +1003,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(), @@ -943,16 +1107,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 +1127,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 +1155,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:keysearch(NewSys2#sys.boot_rel, + #rel.name, + NewSys2#sys.rels) of + {value, _} -> + {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 +1209,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,149 +1220,201 @@ 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) -> @@ -1191,7 +1425,9 @@ decode(#rel{rel_apps = RelApps} = Rel, [RelApp | KeyVals], Status) -> {Name, Type} when is_atom(Name) -> #rel_app{name = Name, app_type = Type, incl_apps = []}; {Name, InclApps} when is_atom(Name), is_list(InclApps) -> - #rel_app{name = Name, app_type = undefined, incl_apps = InclApps}; + #rel_app{name = Name, + app_type = undefined, + incl_apps = InclApps}; {Name, Type, InclApps} when is_atom(Name), is_list(InclApps) -> #rel_app{name = Name, app_type = Type, incl_apps = InclApps}; _ -> @@ -1204,7 +1440,9 @@ decode(#rel{rel_apps = RelApps} = Rel, [RelApp | KeyVals], Status) -> decode(Rel#rel{rel_apps = RelApps ++ [RA]}, KeyVals, Status); true -> 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 +1465,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 +1483,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,47 +1492,53 @@ 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_rel(#rel{name = RelName, rel_apps = RelApps}, #sys{apps = Apps}, Status) -> +check_app({RelName, AppName}, Apps, Status) -> + case lists:keysearch(AppName, #app.name, Apps) of + {value, App} when App#app.is_pre_included -> + Status; + {value, App} when App#app.is_included -> + Status; + _ -> + Text = lists:concat(["Release ", RelName, + " uses non included application ", + AppName]), + reltool_utils:return_first_error(Status, Text) + end. + +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, @@ -1308,13 +1553,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 +1573,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 +1608,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 +1623,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 +1639,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 +1669,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,43 +1691,82 @@ 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) -> @@ -1470,13 +1788,14 @@ merge_escript_app(AppName, Dir, Info, Mods, Apps, OldApps, Status) -> case lists:keysearch(AppName, #app.name, Apps) of {value, _} -> 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]}, @@ -1491,10 +1810,11 @@ merge_app_dirs([{Name, Dir} | Rest], Apps, OldApps) -> {value, OldApp} when OldApp#app.active_dir =:= Dir -> [OldApp | Apps2]; {value, OldApp} -> - App = + 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, @@ -1545,12 +1865,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 +1881,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 +1913,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 +1955,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 +1973,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..dd6f75b9fc 100644 --- a/lib/reltool/src/reltool_target.erl +++ b/lib/reltool/src/reltool_target.erl @@ -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 = +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:keysearch(erts, #app.name, Apps) of {value, Erts} -> - [{erts, gen_config(Erts, InclDefaults)}]; + [{erts, do_gen_config(Erts, InclDefs)}]; false -> [] 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,26 @@ 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) @@ -401,8 +534,8 @@ sort_apps([#app{name = Name, info = Info} = App | Apps], Missing, Circular, Visi %% 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; + Circular1 = case NewCircular of + [] -> Circular; _ -> [Name | NewCircular] ++ Circular end, %% L must be started before N, try again, with all apps @@ -414,11 +547,13 @@ 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)]). @@ -431,24 +566,49 @@ find_all(CheckingApp, [Name | Names], Apps, Visited, Found, NotFound) -> 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 +630,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 +704,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 +712,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 +742,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 +761,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 +784,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 +817,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 +827,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 +839,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 +913,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 +941,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 +956,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 +973,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 +985,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 +998,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 +1024,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 +1060,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 +1103,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 +1112,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 +1158,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 +1178,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 +1204,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) -> @@ -1039,9 +1264,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 +1285,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 +1301,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 +1319,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 +1338,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 +1357,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 +1374,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 +1384,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) -> 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}, diff --git a/lib/reltool/test/Makefile b/lib/reltool/test/Makefile index 00d2add3e5..34781ae720 100644 --- a/lib/reltool/test/Makefile +++ b/lib/reltool/test/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 @@ -25,6 +25,7 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk MODULES= \ rtt \ + reltool_app_SUITE \ reltool_wx_SUITE \ reltool_server_SUITE \ reltool_test_lib diff --git a/lib/reltool/test/reltool_app_SUITE.erl b/lib/reltool/test/reltool_app_SUITE.erl new file mode 100644 index 0000000000..f8433f73d0 --- /dev/null +++ b/lib/reltool/test/reltool_app_SUITE.erl @@ -0,0 +1,291 @@ +%% +%% %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% +%% + +%% +%%---------------------------------------------------------------------- +%% Purpose: Verify the application specifics of the Reltool application +%%---------------------------------------------------------------------- +-module(reltool_app_SUITE). + +-compile(export_all). + +-include("reltool_test_lib.hrl"). + + +t() -> reltool_test_lib:t(?MODULE). +t(Case) -> reltool_test_lib:t({?MODULE, Case}). + +%% Test server callbacks +init_per_suite(Config) -> + Config2 = reltool_test_lib:init_per_suite(Config), + case is_app(reltool) of + {ok, AppFile} -> + %% io:format("AppFile: ~n~p~n", [AppFile]), + [{app_file, AppFile} | Config2]; + {error, Reason} -> + fail(Reason) + end. + +end_per_suite(Config) -> + reltool_test_lib:end_per_suite(Config). + +init_per_testcase(Case, Config) -> + Config2 = + case Case of + undef_funcs -> + [{tc_timeout, timer:minutes(10)} | Config]; + _ -> + Config + end, + reltool_test_lib:init_per_testcase(Case, Config2). + +end_per_testcase(Func,Config) -> + reltool_test_lib:end_per_testcase(Func,Config). + +fin_per_testcase(Case, Config) -> + reltool_test_lib:end_per_testcase(Case, Config). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +all() -> + all(suite). + +all(suite) -> + [ + fields, + modules, + export_all, + app_depend, + undef_funcs + ]. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +is_app(App) -> + LibDir = code:lib_dir(App), + File = filename:join([LibDir, "ebin", atom_to_list(App) ++ ".app"]), + case file:consult(File) of + {ok, [{application, App, AppFile}]} -> + {ok, AppFile}; + {error, {LineNo, Mod, Code}} -> + IoList = lists:concat([File, ":", LineNo, ": ", + Mod:format_error(Code)]), + {error, list_to_atom(lists:flatten(IoList))} + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +fields(suite) -> + []; +fields(doc) -> + []; +fields(Config) when is_list(Config) -> + AppFile = key1search(app_file, Config), + Fields = [vsn, description, modules, registered, applications], + case check_fields(Fields, AppFile, []) of + [] -> + ok; + Missing -> + fail({missing_fields, Missing}) + end. + +check_fields([], _AppFile, Missing) -> + Missing; +check_fields([Field|Fields], AppFile, Missing) -> + check_fields(Fields, AppFile, check_field(Field, AppFile, Missing)). + +check_field(Name, AppFile, Missing) -> + io:format("checking field: ~p~n", [Name]), + case lists:keymember(Name, 1, AppFile) of + true -> + Missing; + false -> + [Name|Missing] + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +modules(suite) -> + []; +modules(doc) -> + []; +modules(Config) when is_list(Config) -> + AppFile = key1search(app_file, Config), + Mods = key1search(modules, AppFile), + EbinList = get_ebin_mods(reltool), + case missing_modules(Mods, EbinList, []) of + [] -> + ok; + Missing -> + throw({error, {missing_modules, Missing}}) + end, + case extra_modules(Mods, EbinList, []) of + [] -> + ok; + Extra -> + throw({error, {extra_modules, Extra}}) + end, + {ok, Mods}. + +get_ebin_mods(App) -> + LibDir = code:lib_dir(App), + EbinDir = filename:join([LibDir,"ebin"]), + {ok, Files0} = file:list_dir(EbinDir), + Files1 = [lists:reverse(File) || File <- Files0], + [list_to_atom(lists:reverse(Name)) || [$m,$a,$e,$b,$.|Name] <- Files1]. + +missing_modules([], _Ebins, Missing) -> + Missing; +missing_modules([Mod|Mods], Ebins, Missing) -> + case lists:member(Mod, Ebins) of + true -> + missing_modules(Mods, Ebins, Missing); + false -> + io:format("missing module: ~p~n", [Mod]), + missing_modules(Mods, Ebins, [Mod|Missing]) + end. + + +extra_modules(_Mods, [], Extra) -> + Extra; +extra_modules(Mods, [Mod|Ebins], Extra) -> + case lists:member(Mod, Mods) of + true -> + extra_modules(Mods, Ebins, Extra); + false -> + io:format("supefluous module: ~p~n", [Mod]), + extra_modules(Mods, Ebins, [Mod|Extra]) + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +export_all(suite) -> + []; +export_all(doc) -> + []; +export_all(Config) when is_list(Config) -> + AppFile = key1search(app_file, Config), + Mods = key1search(modules, AppFile), + check_export_all(Mods). + + +check_export_all([]) -> + ok; +check_export_all([Mod|Mods]) -> + case (catch apply(Mod, module_info, [compile])) of + {'EXIT', {undef, _}} -> + check_export_all(Mods); + O -> + case lists:keysearch(options, 1, O) of + false -> + check_export_all(Mods); + {value, {options, List}} -> + case lists:member(export_all, List) of + true -> + throw({error, {export_all, Mod}}); + false -> + check_export_all(Mods) + end + end + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +app_depend(suite) -> + []; +app_depend(doc) -> + []; +app_depend(Config) when is_list(Config) -> + AppFile = key1search(app_file, Config), + Apps = key1search(applications, AppFile), + check_apps(Apps). + +check_apps([]) -> + ok; +check_apps([App|Apps]) -> + case is_app(App) of + {ok, _} -> + check_apps(Apps); + Error -> + throw({error, {missing_app, {App, Error}}}) + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +undef_funcs(suite) -> + []; +undef_funcs(doc) -> + []; +undef_funcs(Config) when is_list(Config) -> + App = reltool, + AppFile = key1search(app_file, Config), + Mods = key1search(modules, AppFile), + Root = code:root_dir(), + LibDir = code:lib_dir(App), + EbinDir = filename:join([LibDir,"ebin"]), + XRefTestName = undef_funcs_make_name(App, xref_test_name), + {ok, XRef} = xref:start(XRefTestName), + ok = xref:set_default(XRef, + [{verbose,false},{warnings,false}]), + XRefName = undef_funcs_make_name(App, xref_name), + {ok, XRefName} = xref:add_release(XRef, Root, {name,XRefName}), + {ok, App} = xref:replace_application(XRef, App, EbinDir), + {ok, Undefs} = xref:analyze(XRef, undefined_function_calls), + xref:stop(XRef), + analyze_undefined_function_calls(Undefs, Mods, []). + +analyze_undefined_function_calls([], _, []) -> + ok; +analyze_undefined_function_calls([], _, AppUndefs) -> + exit({suite_failed, {undefined_function_calls, AppUndefs}}); +analyze_undefined_function_calls([{{Mod, _F, _A}, _C} = AppUndef|Undefs], + AppModules, AppUndefs) -> + %% Check that this module is our's + case lists:member(Mod,AppModules) of + true -> + {Calling,Called} = AppUndef, + {Mod1,Func1,Ar1} = Calling, + {Mod2,Func2,Ar2} = Called, + io:format("undefined function call: " + "~n ~w:~w/~w calls ~w:~w/~w~n", + [Mod1,Func1,Ar1,Mod2,Func2,Ar2]), + analyze_undefined_function_calls(Undefs, AppModules, + [AppUndef|AppUndefs]); + false -> + io:format("dropping ~p~n", [Mod]), + analyze_undefined_function_calls(Undefs, AppModules, AppUndefs) + end. + +%% This function is used simply to avoid cut-and-paste errors later... +undef_funcs_make_name(App, PostFix) -> + list_to_atom(atom_to_list(App) ++ "_" ++ atom_to_list(PostFix)). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +fail(Reason) -> + exit({suite_failed, Reason}). + +key1search(Key, L) -> + case lists:keysearch(Key, 1, L) of + false -> + fail({not_found, Key, L}); + {value, {Key, Value}} -> + Value + end. diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl index cf951191a0..faf1bdbba2 100644 --- a/lib/reltool/test/reltool_server_SUITE.erl +++ b/lib/reltool/test/reltool_server_SUITE.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_server_SUITE). @@ -26,11 +26,13 @@ -include("reltool_test_lib.hrl"). -define(NODE_NAME, '__RELTOOL__TEMPORARY_TEST__NODE__'). +-define(WORK_DIR, "reltool_work_dir"). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Initialization functions. init_per_suite(Config) -> + ?ignore(file:make_dir(?WORK_DIR)), reltool_test_lib:init_per_suite(Config). end_per_suite(Config) -> @@ -128,8 +130,7 @@ create_release(_Config) -> {value, {_, _, StdlibVsn}} = lists:keysearch(stdlib, 1, Apps), Rel = {release, {RelName, RelVsn}, {erts, ErtsVsn}, - [{kernel, KernelVsn}, - {stdlib, StdlibVsn}]}, + [{kernel, KernelVsn}, {stdlib, StdlibVsn}]}, ?m({ok, Rel}, reltool:get_rel([{config, Config}], RelName)), ok. @@ -159,15 +160,17 @@ create_script(_Config) -> Rel = {release, {RelName, RelVsn}, {erts, ErtsVsn}, - [{stdlib, StdlibVsn}, {kernel, KernelVsn}]}, + [{kernel, KernelVsn}, {stdlib, StdlibVsn}]}, ?m({ok, Rel}, reltool:get_rel(Pid, RelName)), - RelFile = RelName ++ ".rel", - ?m(ok, file:write_file(RelFile, io_lib:format("~p.\n", [Rel]))), + ?m(ok, file:write_file(filename:join([?WORK_DIR, RelName ++ ".rel"]), + io_lib:format("~p.\n", [Rel]))), %% Generate script file + {ok, Cwd} = file:get_cwd(), + ?m(ok, file:set_cwd(?WORK_DIR)), ?m(ok, systools:make_script(RelName, [])), - ScriptFile = RelName ++ ".script", - {ok, [OrigScript]} = ?msym({ok, [_]}, file:consult(ScriptFile)), + {ok, [OrigScript]} = ?msym({ok, [_]}, file:consult(RelName ++ ".script")), + ?m(ok, file:set_cwd(Cwd)), {ok, Script} = ?msym({ok, _}, reltool:get_script(Pid, RelName)), %% OrigScript2 = sort_script(OrigScript), %% Script2 = sort_script(Script), @@ -201,9 +204,10 @@ create_target(_Config) -> ]}, %% Generate target file - TargetDir = "reltool_target_dir_development", + TargetDir = filename:join([?WORK_DIR, "target_development"]), ?m(ok, reltool_utils:recursive_delete(TargetDir)), ?m(ok, file:make_dir(TargetDir)), + ?log("SPEC: ~p\n", [reltool:get_target_spec([{config, Config}])]), ?m(ok, reltool:create_target([{config, Config}], TargetDir)), Erl = filename:join([TargetDir, "bin", "erl"]), @@ -234,7 +238,7 @@ create_embedded(_Config) -> ]}, %% Generate target file - TargetDir = "reltool_target_dir_embedded", + TargetDir = filename:join([?WORK_DIR, "target_embedded"]), ?m(ok, reltool_utils:recursive_delete(TargetDir)), ?m(ok, file:make_dir(TargetDir)), ?m(ok, reltool:create_target([{config, Config}], TargetDir)), @@ -264,7 +268,7 @@ create_standalone(_Config) -> ]}, %% Generate target file - TargetDir = "reltool_target_dir_standalone", + TargetDir = filename:join([?WORK_DIR, "target_standalone"]), ?m(ok, reltool_utils:recursive_delete(TargetDir)), ?m(ok, file:make_dir(TargetDir)), ?m(ok, reltool:create_target([{config, Config}], TargetDir)), @@ -290,6 +294,8 @@ create_standalone(_Config) -> create_old_target(TestInfo) when is_atom(TestInfo) -> reltool_test_lib:tc_info(TestInfo); create_old_target(_Config) -> + ?skip("Old style of target", []), + %% Configure the server RelName1 = "Just testing", RelName2 = "Just testing with SASL", @@ -306,7 +312,7 @@ create_old_target(_Config) -> ]}, %% Generate target file - TargetDir = "reltool_target_dir_old", + TargetDir = filename:join([?WORK_DIR, "target_old_style"]), ?m(ok, reltool_utils:recursive_delete(TargetDir)), ?m(ok, file:make_dir(TargetDir)), ?m(ok, reltool:create_target([{config, Config}], TargetDir)), diff --git a/lib/reltool/test/reltool_test_lib.erl b/lib/reltool/test/reltool_test_lib.erl index 25978294ee..5390b0a75e 100644 --- a/lib/reltool/test/reltool_test_lib.erl +++ b/lib/reltool/test/reltool_test_lib.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_test_lib). @@ -24,9 +24,11 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% init_per_suite(Config) when is_list(Config)-> + global:register_name(reltool_global_logger, group_leader()), incr_timetrap(Config, 5). end_per_suite(Config) when is_list(Config)-> + global:unregister_name(reltool_global_logger), ok. incr_timetrap(Config, Times) -> @@ -130,11 +132,9 @@ wx_end_per_suite(Config) -> init_per_testcase(_Func, Config) when is_list(Config) -> set_kill_timer(Config), - global:register_name(reltool_global_logger, group_leader()), Config. end_per_testcase(_Func, Config) when is_list(Config) -> - global:unregister_name(reltool_global_logger), reset_kill_timer(Config), Config. diff --git a/lib/reltool/test/reltool_test_lib.hrl b/lib/reltool/test/reltool_test_lib.hrl index 93134144ea..b592ebb2f0 100644 --- a/lib/reltool/test/reltool_test_lib.hrl +++ b/lib/reltool/test/reltool_test_lib.hrl @@ -1,23 +1,24 @@ %% %% %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_lib("wx/include/wx.hrl"). +-define(flat_format(Format,Args), lists:flatten(io_lib:format(Format,Args))). -define(log(Format,Args), reltool_test_lib:log(Format,Args,?FILE,?LINE)). -define(warning(Format,Args), ?log("<WARNING>\n " ++ Format,Args)). -define(error(Format,Args), reltool_test_lib:error(Format,Args,?FILE,?LINE)). diff --git a/lib/reltool/test/rtt b/lib/reltool/test/rtt index 2411195338..1f93396196 100755 --- a/lib/reltool/test/rtt +++ b/lib/reltool/test/rtt @@ -1,19 +1,19 @@ #! /bin/sh -f # %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% # Usage: rtt [-cerl] <args to erlang startup script> @@ -23,7 +23,7 @@ while [ $# -gt 0 ]; do case "$1" in "-cerl") shift - emu=cerl + emu="$ERL_TOP/bin/cerl" ;; *) break diff --git a/lib/reltool/vsn.mk b/lib/reltool/vsn.mk index b0561a6110..7bee91d428 100644 --- a/lib/reltool/vsn.mk +++ b/lib/reltool/vsn.mk @@ -16,12 +16,4 @@ # # %CopyrightEnd% -RELTOOL_VSN = 0.5.3 - -TICKETS = OTP-8057 -TICKETS_0_5_2 = OTP-8254 -TICKETS_0_5_1 = OTP-8199 -TICKETS_0_5 = OTP-7949 -TICKETS_0_2_2 = OTP-7999 -TICKETS_2_2_1 = OTP-7840 -TICKETS_0_2 = OTP-7805 +RELTOOL_VSN = 0.5.4 |