diff options
author | Erlang/OTP <[email protected]> | 2010-04-28 04:53:39 +0000 |
---|---|---|
committer | Erlang/OTP <[email protected]> | 2010-04-28 04:53:39 +0000 |
commit | 39e5ca57147c08502806f873c107c77e197a78ab (patch) | |
tree | 91e8cad7d4b2e340c1dd8389b693be20fbb50f87 | |
parent | 9658ff5a3b14bf3189986682023c614077d32143 (diff) | |
parent | e57e7f332fcf9ea26a7f7536b18a24181b680463 (diff) | |
download | otp-39e5ca57147c08502806f873c107c77e197a78ab.tar.gz otp-39e5ca57147c08502806f873c107c77e197a78ab.tar.bz2 otp-39e5ca57147c08502806f873c107c77e197a78ab.zip |
Merge branch 'hawk/reltool' into dev
* hawk/reltool:
Make some cleanups
Ensure that {error, Reason} is returned even when server dies
Introduced a new embedded_app_type option
Removed spurious CDATA in documentation
Automatically include applications that must be started
Add app test SUITE
Add app and appup files to reltool
Add function to return status about the configuration
Improved handling of applications explicitly included releases
Created escript for simplified usage from makefiles
OTP-8590 hawk/reltool
-rw-r--r-- | lib/reltool/bin/reltool.escript | 249 | ||||
-rw-r--r-- | lib/reltool/doc/src/reltool.xml | 185 | ||||
-rw-r--r-- | lib/reltool/doc/src/reltool_examples.xml | 4 | ||||
-rw-r--r-- | lib/reltool/src/Makefile | 42 | ||||
-rw-r--r-- | lib/reltool/src/files.mk | 32 | ||||
-rw-r--r-- | lib/reltool/src/reltool.app.src | 45 | ||||
-rw-r--r-- | lib/reltool/src/reltool.appup.src | 22 | ||||
-rw-r--r-- | lib/reltool/src/reltool.erl | 97 | ||||
-rw-r--r-- | lib/reltool/src/reltool.hrl | 142 | ||||
-rw-r--r-- | lib/reltool/src/reltool_mod_win.erl | 4 | ||||
-rw-r--r-- | lib/reltool/src/reltool_server.erl | 561 | ||||
-rw-r--r-- | lib/reltool/src/reltool_sys_win.erl | 2 | ||||
-rw-r--r-- | lib/reltool/src/reltool_target.erl | 493 | ||||
-rw-r--r-- | lib/reltool/src/reltool_utils.erl | 82 | ||||
-rw-r--r-- | lib/reltool/test/Makefile | 11 | ||||
-rw-r--r-- | lib/reltool/test/reltool_app_SUITE.erl | 291 | ||||
-rw-r--r-- | lib/reltool/test/reltool_server_SUITE.erl | 38 | ||||
-rw-r--r-- | lib/reltool/test/reltool_test_lib.erl | 14 | ||||
-rw-r--r-- | lib/reltool/test/reltool_test_lib.hrl | 11 | ||||
-rwxr-xr-x | lib/reltool/test/rtt | 12 |
20 files changed, 1605 insertions, 732 deletions
diff --git a/lib/reltool/bin/reltool.escript b/lib/reltool/bin/reltool.escript new file mode 100644 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/reltool.xml b/lib/reltool/doc/src/reltool.xml index 9786928ae8..0c2b7d2a2b 100644 --- a/lib/reltool/doc/src/reltool.xml +++ b/lib/reltool/doc/src/reltool.xml @@ -67,14 +67,14 @@ <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,27 +97,27 @@ <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 @@ -134,28 +134,28 @@ 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 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> </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 @@ -165,38 +165,38 @@ 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 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> </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 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,7 +221,7 @@ 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 @@ -235,7 +235,7 @@ <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 @@ -245,7 +245,7 @@ <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,7 +256,7 @@ 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 @@ -267,7 +267,7 @@ <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 @@ -280,7 +280,7 @@ 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 @@ -291,7 +291,7 @@ 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 @@ -307,7 +307,7 @@ 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> @@ -318,62 +318,62 @@ 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> </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> @@ -384,18 +384,18 @@ 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 not is included. + <c>derived</c> implies that the is included if any included uses the + 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 @@ -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> 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/src/Makefile b/lib/reltool/src/Makefile index 7fac7cbf88..4e6a112b7e 100644 --- a/lib/reltool/src/Makefile +++ b/lib/reltool/src/Makefile @@ -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 # ---------------------------------------------------- @@ -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 e6a8bca069..9dd0a24f46 100644 --- a/lib/reltool/src/reltool.erl +++ b/lib/reltool/src/reltool.erl @@ -20,9 +20,8 @@ %% 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, install/2 @@ -32,39 +31,26 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% 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()}}. @@ -110,20 +96,48 @@ stop(Pid) when is_pid(Pid) -> end. %% Internal library function --spec eval_server(server(), fun((server_pid()) -> term())) -> +-spec eval_server(server(), boolean(), fun((server_pid()) -> term())) -> {ok, server_pid()} | {error, reason()}. -eval_server(Pid, Fun) when is_pid(Pid) -> +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) -> @@ -133,7 +147,7 @@ get_config(PidOrOption) -> {ok, config()} | {error, reason()}. get_config(PidOrOptions, InclDef, InclDeriv) when is_pid(PidOrOptions); is_list(PidOrOptions) -> - eval_server(PidOrOptions, + eval_server(PidOrOptions, true, fun(Pid) -> reltool_server:get_config(Pid, InclDef, InclDeriv) end). @@ -142,7 +156,7 @@ get_config(PidOrOptions, InclDef, InclDeriv) -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, + eval_server(PidOrOptions, true, fun(Pid) -> reltool_server:get_rel(Pid, RelName) end). %% Get contents of boot script file @@ -150,21 +164,22 @@ get_rel(PidOrOptions, RelName) {ok, script_file()} | {error, reason()}. get_script(PidOrOptions, RelName) when is_pid(PidOrOptions); is_list(PidOrOptions) -> - eval_server(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, + 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). + 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()) -> diff --git a/lib/reltool/src/reltool.hrl b/lib/reltool/src/reltool.hrl index 8a6a2142fd..1a34ced89d 100644 --- a/lib/reltool/src/reltool.hrl +++ b/lib/reltool/src/reltool.hrl @@ -17,7 +17,7 @@ %% %CopyrightEnd% -define(APPLICATION, reltool). --define(MISSING_APP, '*MISSING*'). +-define(MISSING_APP_NAME, '*MISSING*'). -define(MISSING_APP_TEXT, "*MISSING*"). -type file() :: string(). @@ -104,7 +104,7 @@ -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(). @@ -112,10 +112,7 @@ -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()]} + | {archive, base_file(), [archive_opt()], [target_spec()]} | {copy_file, base_file()} | {copy_file, base_file(), top_file()} | {write_file, base_file(), iolist()} @@ -139,57 +136,58 @@ -record(mod, {%% Static - name :: mod_name(), - app_name :: app_name(), - incl_cond :: incl_cond() | undefined, - debug_info :: debug_info() | undefined, - is_app_mod :: boolean(), + 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(), + uses_mods :: [mod_name()], + exists :: boolean(), + %% Dynamic - status :: status(), - used_by_mods :: [mod_name()], + status :: status(), + used_by_mods :: [mod_name()], is_pre_included :: boolean() | undefined, - is_included :: boolean() | undefined + is_included :: boolean() | undefined }). -record(app_info, { - description = "", - id = "", - vsn = "", - modules = [], - maxP = infinity, - maxT = infinity, - registered = [], - incl_apps = [], - applications = [], - env = [], - mod = undefined, - start_phases = undefined + 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 :: app_name(), - is_escript :: boolean(), + 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{}], + active_dir :: dir(), + sorted_dirs :: [dir()], + vsn :: app_vsn(), + label :: app_label(), + info :: #app_info{} | undefined, + mods :: [#mod{}], %% Static source cond - mod_cond :: mod_cond() | undefined, + mod_cond :: mod_cond() | undefined, incl_cond :: incl_cond() | undefined, %% Static target cond - debug_info :: debug_info() | undefined, - app_file :: app_file() | undefined, - app_type :: app_type(), + 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(), @@ -197,19 +195,20 @@ archive_opts :: [archive_opt()], %% Dynamic - status :: status(), - uses_mods :: [mod_name()], - used_by_mods :: [mod_name()], - uses_apps :: [app_name()], - used_by_apps :: [app_name()], + 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() + is_included :: boolean(), + rels :: [rel_name()] }). -record(rel_app, { - name :: app_name(), - app_type :: app_type(), + name :: app_name(), + app_type :: app_type(), incl_apps :: [incl_app()] }). @@ -231,21 +230,22 @@ 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(), + 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(), - app_type :: app_type(), - app_file :: app_file(), - debug_info :: debug_info() + 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}). @@ -268,7 +268,8 @@ -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). @@ -282,22 +283,23 @@ -define(DEFAULT_EXCL_APP_FILTERS, []). -define(EMBEDDED_INCL_SYS_FILTERS, ["^bin", - "^erts", - "^lib", - "^releases"]). + "^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\$"]). + "^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)\$", diff --git a/lib/reltool/src/reltool_mod_win.erl b/lib/reltool/src/reltool_mod_win.erl index c2544cc2d8..f68c61fd6f 100644 --- a/lib/reltool/src/reltool_mod_win.erl +++ b/lib/reltool/src/reltool_mod_win.erl @@ -514,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 @@ -674,7 +674,7 @@ 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, _} -> diff --git a/lib/reltool/src/reltool_server.erl b/lib/reltool/src/reltool_server.erl index a7064f7651..039ad56aa8 100644 --- a/lib/reltool/src/reltool_server.erl +++ b/lib/reltool/src/reltool_server.erl @@ -132,19 +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) -> @@ -161,33 +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 = [], @@ -199,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 -> @@ -261,6 +260,7 @@ loop(#state{common = C, sys = Sys} = S) -> {ok, _Warnings} -> S5#state{status = Status3, old_status = S#state.status}; {error, _} -> + %% Keep old state S end, reltool_utils:reply(ReplyTo, Ref, Status3), @@ -277,9 +277,9 @@ loop(#state{common = C, sys = Sys} = S) -> 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); @@ -292,7 +292,7 @@ loop(#state{common = C, sys = Sys} = S) -> 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); @@ -302,7 +302,7 @@ 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); @@ -312,7 +312,8 @@ loop(#state{common = C, sys = Sys} = S) -> {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); @@ -327,6 +328,7 @@ 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; @@ -372,16 +374,20 @@ loop(#state{common = C, sys = Sys} = S) -> (Sys2#sys.lib_dirs =/= Sys#sys.lib_dirs) orelse (Sys2#sys.escripts =/= Sys#sys.escripts), {S3, Status} = refresh(S2, Force, {ok, []}), - {S4, Status2} = analyse(S3#state{old_sys = S#state.sys}, Status), - 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); @@ -427,15 +433,24 @@ 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), + {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 [] -> @@ -452,17 +467,59 @@ analyse(#state{common = C, sys = #sys{apps = Apps0} = Sys} = S, Status) -> 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)]), + %% [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) -> +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; @@ -473,24 +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) + Default) end, Mods), - %%app_mod_init_is_included(C, AppName, Info, ModCond, AppCond), - A2. + {A2, Status2}. mod_init_is_included(C, M, ModCond, AppCond, Default) -> %% print(M#mod.name, hipe, "incl_cond -> ~p\n", [AppCond]), @@ -635,7 +705,7 @@ mod_mark_is_included(C, Sys, UsedByName, [ModName | ModNames], Acc) -> 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}), @@ -646,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, @@ -672,8 +742,6 @@ mod_propagate_is_used_by(_C, []) -> read_apps(C, Sys, [#app{mods = Mods, is_included = IsIncl} = A | Apps], Acc) -> {Mods2, IsIncl2} = read_apps(C, Sys, A, Mods, [], IsIncl), - %% reltool_utils:print(A#app.name, stdlib, "Mods2: ~p\n", - %% [[M#mod.status || M <- Mods2]]), Status = case lists:keysearch(missing, #mod.status, Mods2) of {value, _} -> missing; @@ -736,9 +804,7 @@ 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} || + exists = false} || M <- A#app.mods, M#mod.incl_cond =/= undefined], if @@ -747,8 +813,7 @@ 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, @@ -778,8 +843,7 @@ filter_app(A) -> label = undefined, info = undefined, mods = Mods, - uses_mods = undefined, - is_included = undefined}} + uses_mods = undefined}} end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -816,16 +880,16 @@ refresh_app(#app{name = AppName, 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 + AppInfoMods end, MissingMods = add_missing_mods(AppName, EbinMods, AppModNames), @@ -862,11 +926,15 @@ read_app_info(AppFileOrBin, AppFile, AppName, DefaultVsn, Status) -> parse_app_info(AppFile, Info, AI, Status); {ok, _BadApp} -> Text = lists:concat([AppName, - ": Illegal contents in app file ", AppFile]), + ": 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-> - {missing_app_info(DefaultVsn), Status}; + {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 ", @@ -1045,7 +1113,7 @@ do_get_config(S, InclDef, InclDeriv) -> false -> shrink_sys(S); true -> S end, - {ok, reltool_target:gen_config(S2#state.sys, InclDef)}. + reltool_target:gen_config(S2#state.sys, InclDef). do_save_config(S, Filename, InclDef, InclDeriv) -> {ok, Config} = do_get_config(S, InclDef, InclDeriv), @@ -1072,6 +1140,7 @@ do_load_config(S, SysConfig) -> {ok, _Warnings2} -> S3#state{status = Status3, old_status = S#state.status}; {error, _} -> + %% Keep old state S end, {S4, Status3}; @@ -1093,29 +1162,36 @@ read_config(OldSys, Filename, Status) when is_list(Filename) -> {error, Reason} -> Text = file:format_error(Reason), {OldSys, - reltool_utils:return_first_error(Status, "File access: " ++ - Text)} + 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])), @@ -1160,120 +1236,96 @@ decode(#sys{} = Sys, [{Key, Val} | KeyVals], Status) -> {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; + 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; + 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)}, + 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)}, + 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)}, + 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)}, + 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)}, + 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)}, + 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}; + 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 -> @@ -1303,38 +1355,39 @@ decode(#app{} = App, [{Key, Val} | KeyVals], Status) -> Val =:= strip -> {App#app{debug_info = Val}, Status}; app_file when Val =:= keep; - Val =:= strip, + Val =:= strip; Val =:= all -> {App#app{app_file = Val}, Status}; app_type when Val =:= permanent; Val =:= transient; Val =:= temporary; Val =:= load; - Val =:= none -> + 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)}, + 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)}, + 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)}, + 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)}, + dec_re(Key, + Val, + App#app.excl_archive_filters)}, Status}; archive_opts when is_list(Val) -> {App#app{archive_opts = Val}, Status}; @@ -1439,54 +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 +verify_config(RelApps, #sys{boot_rel = BootRel, rels = Rels, apps = Apps}, Status) -> + case lists:keymember(BootRel, #rel.name, Rels) of true -> - lists:foreach(fun(Rel)-> check_rel(Rel, Sys, Status) end, - Sys#sys.rels), - Status; + Status2 = lists:foldl(fun(RA, Acc) -> + check_app(RA, Apps, Acc) end, + Status, + RelApps), + lists:foldl(fun(#rel{name = RelName}, Acc)-> + check_rel(RelName, RelApps, Acc) + end, + Status2, + Rels); false -> - Text = lists:concat([Sys#sys.boot_rel, ": release is mandatory"]), - Status2 = reltool_utils:return_first_error(Status, Text), - throw({error, Status2}) + Text = lists:concat(["Release ", BootRel, + " is mandatory (used as boot_rel)"]), + reltool_utils:return_first_error(Status, Text) + end. + +check_app({RelName, AppName}, Apps, Status) -> + case lists:keysearch(AppName, #app.name, Apps) of + {value, App} when App#app.is_pre_included -> + Status; + {value, App} when App#app.is_included -> + Status; + _ -> + Text = lists:concat(["Release ", RelName, + " uses non included application ", + AppName]), + reltool_utils:return_first_error(Status, Text) end. -check_rel(#rel{name = RelName, rel_apps = RelApps}, - #sys{apps = Apps}, - Status) -> +check_rel(RelName, RelApps, Status) -> EnsureApp = - fun(AppName) -> - case lists:keymember(AppName, #rel_app.name, RelApps) of + fun(AppName, Acc) -> + case lists:member({RelName, AppName}, RelApps) of true -> - ok; + Acc; false -> - Text = lists:concat([RelName, ": ", AppName, - " is not included."]), - Status2 = - reltool_utils:return_first_error(Status, Text), - throw({error, Status2}) - end - end, - EnsureApp(kernel), - EnsureApp(stdlib), - CheckRelApp = - fun(#rel_app{name = AppName}) -> - case lists:keysearch(AppName, #app.name, Apps) of - {value, App} when App#app.is_pre_included -> - ok; - {value, App} when App#app.is_included -> - ok; - _ -> - Text = - lists:concat([RelName, ": uses application ", - AppName, " that not is included."]), - Status2 = - reltool_utils:return_first_error(Status, Text), - %% throw BUGBUG: add throw - ({error, Status2}) + Text = lists:concat(["Mandatory application ", + AppName, + " is not included in release ", + RelName]), + reltool_utils:return_first_error(Acc, Text) end end, - lists:foreach(CheckRelApp, RelApps). + Mandatory = [kernel, stdlib], + lists:foldl(EnsureApp, Status, Mandatory). patch_erts_version(RootDir, Apps, Status) -> AppName = erts, @@ -1507,7 +1559,7 @@ patch_erts_version(RootDir, Apps, Status) -> {Apps, Status} end; false -> - Text = "erts cannnot be found in the root directory " ++ RootDir, + Text = "erts cannot be found in the root directory " ++ RootDir, Status2 = reltool_utils:return_first_error(Status, Text), {Apps, Status2} end. @@ -1813,7 +1865,8 @@ 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) @@ -1842,7 +1895,7 @@ 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 -> @@ -1860,7 +1913,7 @@ 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 -> diff --git a/lib/reltool/src/reltool_sys_win.erl b/lib/reltool/src/reltool_sys_win.erl index 2e226d9147..dbb8e32aa2 100644 --- a/lib/reltool/src/reltool_sys_win.erl +++ b/lib/reltool/src/reltool_sys_win.erl @@ -1227,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], diff --git a/lib/reltool/src/reltool_target.erl b/lib/reltool/src/reltool_target.erl index e079a02f3c..dd6f75b9fc 100644 --- a/lib/reltool/src/reltool_target.erl +++ b/lib/reltool/src/reltool_target.erl @@ -31,7 +31,7 @@ gen_target/2, install/2 ]). --compile(export_all). + -include("reltool.hrl"). -include_lib("kernel/include/file.hrl"). @@ -64,54 +64,60 @@ kernel_processes(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}, - InclDefs) -> +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, InclDefs)}]; + [{erts, do_gen_config(Erts, InclDefs)}]; false -> [] end, AppsItems = - [{app, A#app.name, gen_config(A, InclDefs)} + [do_gen_config(A, InclDefs) || A <- Apps, - A#app.name =/= ?MISSING_APP, + A#app.name =/= ?MISSING_APP_NAME, A#app.name =/= erts, - A#app.is_included =:= true, - A#app.is_escript =/= true], + 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], + || A <- Apps, A#app.is_escript], DefaultRels = reltool_utils:default_rels(), RelsItems = - case {[{rel, R#rel.name, R#rel.vsn, gen_config(R, InclDefs)} || - R <- Rels], - [{rel, R#rel.name, R#rel.vsn, gen_config(R, InclDefs)} || - 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(), InclDefs) ++ @@ -120,78 +126,103 @@ gen_config(#sys{root_dir = RootDir, emit(mod_cond, ModCond, ?DEFAULT_MOD_COND, InclDefs) ++ emit(incl_cond, AppCond, ?DEFAULT_INCL_COND, InclDefs) ++ ErtsItems ++ - AppsItems ++ + lists:flatten(AppsItems) ++ emit(boot_rel, BootRel, ?DEFAULT_REL_NAME, InclDefs) ++ - RelsItems ++ + 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), ?DEFAULT_INCL_SYS_FILTERS, InclDefs) ++ - emit(excl_sys_filters, X(ExclSysFiles), ?DEFAULT_EXCL_SYS_FILTERS, InclDefs) ++ - emit(incl_app_filters, X(InclAppFiles), ?DEFAULT_INCL_APP_FILTERS, InclDefs) ++ - emit(excl_app_filters, X(ExclAppFiles), ?DEFAULT_EXCL_APP_FILTERS, 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(app_type, AppType, ?DEFAULT_APP_TYPE, 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)}; -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}, - InclDefs) -> - 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) ++ - emit(vsn, Vsn, undefined, InclDefs orelse UseSelected =/= true) ++ - [{mod, M#mod.name, gen_config(M, InclDefs)} || - M <- Mods, - M#mod.is_included =:= true]; -gen_config(#mod{name = _Name, - incl_cond = AppCond, - debug_info = DebugInfo}, - InclDefs) -> - emit(incl_cond, AppCond, undefined, InclDefs) ++ - emit(debug_info, DebugInfo, undefined, InclDefs); -gen_config(#rel{name = _Name, - vsn = _Vsn, - rel_apps = RelApps}, - InclDefs) -> - [gen_config(RA, InclDefs) || RA <- RelApps]; -gen_config(#rel_app{name = Name, - app_type = Type, - incl_apps = InclApps}, - _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}, InclDefs) -> +do_gen_config({Tag, Val}, InclDefs) -> emit(Tag, Val, undefined, InclDefs); -gen_config([], _InclDefs) -> +do_gen_config([], _InclDefs) -> []; -gen_config([H | T], InclDefs) -> - lists:flatten([gen_config(H, InclDefs), gen_config(T, InclDefs)]). +do_gen_config([H | T], InclDefs) -> + lists:flatten([do_gen_config(H, InclDefs), do_gen_config(T, InclDefs)]). emit(Tag, Val, Default, InclDefs) -> + %% io:format("~p(~p):\n\t~p\n\t~p\n", + %% [Tag, Val =/= Default, Val, Default]), if Val == undefined -> []; InclDefs -> [{Tag, Val}]; @@ -239,24 +270,127 @@ 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]}. - -app_to_rel(#rel_app{name = Name, app_type = Type, incl_apps = InclApps}, - Apps) -> - {value, #app{vsn = Vsn}} = lists:keysearch(Name, #app.name, Apps), +gen_rel(Rel, Sys) -> + try + MergedApps = merge_apps(Rel, Sys), + {ok, do_gen_rel(Rel, Sys, MergedApps)} + catch + throw:{error, Text} -> + {error, Text} + end. + +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. +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 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -270,27 +404,24 @@ gen_boot({script, {_, _}, _} = Script) -> gen_script(Rel, Sys, PathFlag, Variables) -> try - do_gen_script(Rel, Sys, PathFlag, Variables) + 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 = @@ -300,30 +431,30 @@ 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, + #app{name = Name, app_type = Type} <- MergedApps, Type =/= none, Type =/= load, not lists:member(Name, InclApps)], @@ -334,28 +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) -> @@ -438,13 +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", + reltool_utils:throw_error("Undefined applications: ~p", [make_set(Missing)]); sort_apps([], [], Circular, _) -> - reltool_utils:throw_error("Circular dependencies: ~p\n", + 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)]). @@ -603,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(), @@ -665,8 +775,6 @@ do_gen_spec(#sys{root_dir = RootDir, {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), @@ -678,23 +786,25 @@ strip_sys_files(Relocatable, SysFiles, Apps, ExclRegexps) -> true -> ExtraExcl = ["^erts.*/bin/.*src\$"], reltool_utils:decode_regexps(excl_sys_filters, - {add, ExtraExcl}, ExclRegexps); + {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 -> + 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, @@ -707,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 @@ -759,20 +870,31 @@ spec_bin_files(Sys, AllSysFiles, StrippedSysFiles, RelFiles, InclRegexps) -> GoodNames = [F || {copy_file, F} <- OldBinFiles, 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; @@ -796,7 +918,6 @@ 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 " @@ -820,6 +941,7 @@ lookup_spec(Prefix, Specs) -> safe_lookup_spec(Prefix, Specs) -> case lookup_spec(Prefix, Specs) of [] -> + %% io:format("lookup fail ~s:\n\t~p\n", [Prefix, Specs]), reltool_utils:throw_error("Mandatory system file ~s is " "not included", [Prefix]); Match -> @@ -834,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; @@ -863,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), @@ -942,13 +1064,13 @@ spec_dir(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) -> @@ -1082,7 +1204,7 @@ do_eval_spec({archive, Archive, Options, Files}, {ok, _} -> ok; {error, Reason} -> - reltool_utils:throw_error("create archive ~s: ~p\n", + reltool_utils:throw_error("create archive ~s failed: ~p", [ArchiveFile, Reason]) end; do_eval_spec({copy_file, File}, _OrigSourceDir, SourceDir, TargetDir) -> @@ -1216,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, []) -> @@ -1284,7 +1395,7 @@ do_install(RelName, TargetDir) -> 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) -> diff --git a/lib/reltool/src/reltool_utils.erl b/lib/reltool/src/reltool_utils.erl index 403fa574c5..39d057d994 100644 --- a/lib/reltool/src/reltool_utils.erl +++ b/lib/reltool/src/reltool_utils.erl @@ -19,7 +19,30 @@ -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"). @@ -103,18 +126,46 @@ 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) -> @@ -379,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) -> @@ -388,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) -> @@ -397,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) -> @@ -406,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) -> @@ -415,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) -> @@ -424,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) -> @@ -560,7 +611,12 @@ escript_foldl(Fun, Acc, File) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 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 |