aboutsummaryrefslogtreecommitdiffstats
path: root/lib/reltool
diff options
context:
space:
mode:
Diffstat (limited to 'lib/reltool')
-rw-r--r--lib/reltool/src/reltool.hrl2
-rw-r--r--lib/reltool/src/reltool_server.erl30
-rw-r--r--lib/reltool/src/reltool_target.erl72
-rw-r--r--lib/reltool/src/reltool_utils.erl8
-rw-r--r--lib/reltool/test/reltool_server_SUITE.erl95
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/sort_apps/z-1.0/ebin/z.app4
6 files changed, 121 insertions, 90 deletions
diff --git a/lib/reltool/src/reltool.hrl b/lib/reltool/src/reltool.hrl
index fc0078715a..4e5c5b2849 100644
--- a/lib/reltool/src/reltool.hrl
+++ b/lib/reltool/src/reltool.hrl
@@ -206,7 +206,7 @@
{
name :: app_name(),
app_type :: app_type() | undefined,
- incl_apps = [] :: [incl_app()]
+ incl_apps :: [incl_app()] | undefined
}).
-record(rel,
diff --git a/lib/reltool/src/reltool_server.erl b/lib/reltool/src/reltool_server.erl
index 29df619955..c99180a613 100644
--- a/lib/reltool/src/reltool_server.erl
+++ b/lib/reltool/src/reltool_server.erl
@@ -560,10 +560,30 @@ apps_in_rels(Rels, Apps) ->
apps_in_rel(#rel{name = RelName, rel_apps = RelApps}, Apps) ->
Mandatory = [{RelName, kernel}, {RelName, stdlib}],
- Other = [{RelName, AppName} ||
- RA <- RelApps,
- AppName <- [RA#rel_app.name | RA#rel_app.incl_apps],
- not lists:keymember(AppName, 2, Mandatory)],
+ Other =
+ [{RelName, AppName} ||
+ RA <- RelApps,
+ AppName <- [RA#rel_app.name |
+ %% Included applications in rel shall overwrite included
+ %% applications in .app. I.e. included applications in
+ %% .app shall only be used if it is not defined in rel.
+ case RA#rel_app.incl_apps of
+ undefined ->
+ case lists:keyfind(RA#rel_app.name,
+ #app.name,
+ Apps) of
+ #app{info = #app_info{incl_apps = IA}} ->
+ IA;
+ false ->
+ reltool_utils:throw_error(
+ "Release ~p uses non existing "
+ "application ~p",
+ [RelName,RA#rel_app.name])
+ end;
+ IA ->
+ IA
+ end],
+ not lists:keymember(AppName, 2, Mandatory)],
more_apps_in_rels(Mandatory ++ Other, Apps, []).
more_apps_in_rels([{RelName, AppName} = RA | RelApps], Apps, Acc) ->
@@ -1505,7 +1525,7 @@ decode(#rel{rel_apps = RelApps} = Rel, [RelApp | KeyVals]) ->
{VT andalso VI,
#rel_app{name = Name, app_type = Type, incl_apps = InclApps}};
_ ->
- {false, #rel_app{incl_apps = []}}
+ {false, #rel_app{}}
end,
case ValidTypesAssigned of
true ->
diff --git a/lib/reltool/src/reltool_target.erl b/lib/reltool/src/reltool_target.erl
index 40d1009733..3d83a77d99 100644
--- a/lib/reltool/src/reltool_target.erl
+++ b/lib/reltool/src/reltool_target.erl
@@ -208,10 +208,10 @@ do_gen_config(#rel_app{name = Name,
incl_apps = InclApps},
_InclDefs) ->
case {Type, InclApps} of
- {undefined, []} -> Name;
- {undefined, _} -> {Name, InclApps};
- {_, []} -> {Name, Type};
- {_, _} -> {Name, Type, InclApps}
+ {undefined, undefined} -> Name;
+ {undefined, _} -> {Name, InclApps};
+ {_, undefined} -> {Name, Type};
+ {_, _} -> {Name, Type, InclApps}
end;
do_gen_config({Tag, Val}, InclDefs) ->
emit(Tag, Val, undefined, InclDefs);
@@ -279,7 +279,7 @@ gen_rel(Rel, Sys) ->
{error, Text}
end.
-do_gen_rel(#rel{name = RelName, vsn = RelVsn},
+do_gen_rel(#rel{name = RelName, vsn = RelVsn, rel_apps = RelApps},
#sys{apps = Apps},
MergedApps) ->
ErtsName = erts,
@@ -288,7 +288,7 @@ do_gen_rel(#rel{name = RelName, vsn = RelVsn},
{release,
{RelName, RelVsn},
{ErtsName, Erts#app.vsn},
- [strip_rel_info(App) || App <- MergedApps]};
+ [strip_rel_info(App, RelApps) || App <- MergedApps]};
false ->
reltool_utils:throw_error("Mandatory application ~p is "
"not included",
@@ -298,13 +298,17 @@ do_gen_rel(#rel{name = RelName, vsn = RelVsn},
strip_rel_info(#app{name = Name,
vsn = Vsn,
app_type = Type,
- info = #app_info{incl_apps = InclApps}})
- when Type =/= undefined ->
- case {Type, InclApps} of
- {permanent, []} -> {Name, Vsn};
- {permanent, _} -> {Name, Vsn, InclApps};
- {_, []} -> {Name, Vsn, Type};
- {_, _} -> {Name, Vsn, Type, InclApps}
+ info = #app_info{incl_apps = AppInclApps}},
+ RelApps) when Type =/= undefined ->
+ RelInclApps = case lists:keyfind(Name,#rel_app.name,RelApps) of
+ #rel_app{incl_apps = RIA} when RIA =/= undefined -> RIA;
+ _ -> undefined
+ end,
+ case {Type, RelInclApps} of
+ {permanent, undefined} -> {Name, Vsn};
+ {permanent, _} -> {Name, Vsn, AppInclApps};
+ {_, undefined} -> {Name, Vsn, Type};
+ {_, _} -> {Name, Vsn, Type, AppInclApps}
end.
merge_apps(#rel{name = RelName,
@@ -323,7 +327,7 @@ merge_apps(#rel{name = RelName,
A#app.name =/= ?MISSING_APP_NAME,
not lists:keymember(A#app.name, #app.name, MergedApps2)],
MergedApps3 = do_merge_apps(RelName, Embedded, Apps, EmbAppType, MergedApps2),
- sort_apps(MergedApps3).
+ sort_apps(lists:reverse(MergedApps3)).
do_merge_apps(RelName, [#rel_app{name = Name} = RA | RelApps], Apps, RelAppType, Acc) ->
case is_already_merged(Name, RelApps, Acc) of
@@ -341,25 +345,18 @@ do_merge_apps(RelName, [Name | RelApps], Apps, RelAppType, Acc) ->
true ->
do_merge_apps(RelName, RelApps, Apps, RelAppType, Acc);
false ->
- RelApp = init_rel_app(Name, Apps),
+ RelApp = #rel_app{name = Name},
do_merge_apps(RelName, [RelApp | RelApps], Apps, RelAppType, Acc)
end;
do_merge_apps(_RelName, [], _Apps, _RelAppType, Acc) ->
- lists:reverse(Acc).
-
-init_rel_app(Name, Apps) ->
- {value, App} = lists:keysearch(Name, #app.name, Apps),
- Info = App#app.info,
- #rel_app{name = Name,
- app_type = undefined,
- incl_apps = Info#app_info.incl_apps}.
+ Acc.
merge_app(RelName,
- #rel_app{name = Name,
- app_type = Type,
- incl_apps = InclApps},
- RelAppType,
- App) ->
+ #rel_app{name = Name,
+ app_type = Type,
+ incl_apps = InclApps0},
+ RelAppType,
+ App) ->
Type2 =
case {Type, App#app.app_type} of
{undefined, undefined} -> RelAppType;
@@ -367,6 +364,11 @@ merge_app(RelName,
{_, _} -> Type
end,
Info = App#app.info,
+ InclApps =
+ case InclApps0 of
+ undefined -> Info#app_info.incl_apps;
+ _ -> InclApps0
+ end,
case InclApps -- Info#app_info.incl_apps of
[] ->
App#app{app_type = Type2, info = Info#app_info{incl_apps = InclApps}};
@@ -421,7 +423,10 @@ do_gen_script(#rel{name = RelName, vsn = RelVsn},
Mandatory = mandatory_modules(),
Early = Mandatory ++ Preloaded,
{value, KernelApp} = lists:keysearch(kernel, #app.name, MergedApps),
- InclApps = [I || #app{info = #app_info{incl_apps = I}} <- MergedApps],
+ InclApps = lists:flatmap(fun(#app{info = #app_info{incl_apps = I}}) ->
+ I
+ end,
+ MergedApps),
%% Create the script
DeepList =
@@ -471,7 +476,7 @@ load_app_mods(#app{mods = Mods} = App, Mand, PathFlag, Variables) ->
Path = cr_path(App, PathFlag, Variables),
PartNames =
lists:sort([{packages:split(M),M} ||
- #mod{name = M} <- Mods,
+ #mod{name = M, is_included=true} <- Mods,
not lists:member(M, Mand)]),
SplitMods =
lists:foldl(
@@ -513,7 +518,12 @@ sort_apps([#app{name = Name, info = Info} = App | Apps],
Circular,
Visited) ->
{Uses, Apps1, NotFnd1} =
- find_all(Name, Info#app_info.applications, Apps, Visited, [], []),
+ find_all(Name,
+ lists:reverse(Info#app_info.applications),
+ Apps,
+ Visited,
+ [],
+ []),
{Incs, Apps2, NotFnd2} =
find_all(Name,
lists:reverse(Info#app_info.incl_apps),
diff --git a/lib/reltool/src/reltool_utils.erl b/lib/reltool/src/reltool_utils.erl
index 9cf9bd1418..3e50324011 100644
--- a/lib/reltool/src/reltool_utils.erl
+++ b/lib/reltool/src/reltool_utils.erl
@@ -126,18 +126,14 @@ prim_parse(Tokens, Acc) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
default_rels() ->
- %%Kernel = #rel_app{name = kernel, incl_apps = []},
- %%Stdlib = #rel_app{name = stdlib, incl_apps = []},
- Sasl = #rel_app{name = sasl, incl_apps = []},
+ %% kernel and stdlib are added automatically in every release
[
#rel{name = ?DEFAULT_REL_NAME,
vsn = "1.0",
rel_apps = []},
- %%rel_apps = [Kernel, Stdlib]},
#rel{name = "start_sasl",
vsn = "1.0",
- rel_apps = [Sasl]}
- %%rel_apps = [Kernel, Sasl, Stdlib]}
+ rel_apps = [#rel_app{name = sasl}]}
].
choose_default(Tag, Profile, InclDefs)
diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl
index 57b04251d4..122880fca9 100644
--- a/lib/reltool/test/reltool_server_SUITE.erl
+++ b/lib/reltool/test/reltool_server_SUITE.erl
@@ -283,7 +283,7 @@ create_release(_Config) ->
%% started before the including application.
%% Circular dependencies shall also be detected and cause error.
-create_release_sort(_Config) -> {skip, "Multiple known problems - see OTP-9792"};
+create_release_sort(_Config) -> {skip, "Two bugs related to sorting"};
create_release_sort(Config) ->
DataDir = ?config(data_dir,Config),
%% Configure the server
@@ -296,7 +296,11 @@ create_release_sort(Config) ->
RelName7 = "Circular",
RelName8 = "Include-both-missing-app",
RelName9 = "Include-overwrite",
+ RelName10= "Uses-order-as-rel",
RelVsn = "1.0",
+ %% Application z (.app file):
+ %% includes [tools, mnesia]
+ %% uses [kernel, stdlib, sasl, inets]
Sys =
{sys,
[
@@ -304,19 +308,21 @@ create_release_sort(Config) ->
{boot_rel, RelName1},
{rel, RelName1, RelVsn, [stdlib, kernel, mnesia, sasl]},
{rel, RelName2, RelVsn, [stdlib, kernel, sasl, mnesia]},
- {rel, RelName3, RelVsn, [stdlib, kernel, {z,[tools]}, tools]},
- {rel, RelName4, RelVsn, [stdlib, kernel, z, tools]},
+ {rel, RelName3, RelVsn, [stdlib, kernel, {z,[tools]}, tools, mnesia]},
+ {rel, RelName4, RelVsn, [stdlib, kernel, z, mnesia, tools]},
{rel, RelName5, RelVsn, [stdlib, kernel, {sasl,[tools]}]},
- {rel, RelName6, RelVsn, [stdlib, kernel, z]}, %z includes tools in .app
+ {rel, RelName6, RelVsn, [stdlib, kernel, z]},
{rel, RelName7, RelVsn, [stdlib, kernel, mnesia, y, sasl, x]},
{rel, RelName8, RelVsn, [stdlib, kernel, {z,[tools]}]},
{rel, RelName9, RelVsn, [stdlib, kernel, {z,[]}]},
+ {rel, RelName10, RelVsn, [stdlib, kernel, {z,[]}, inets, sasl]},
{incl_cond,exclude},
{mod_cond,app},
{app,kernel,[{incl_cond,include}]},
{app,stdlib,[{incl_cond,include}]},
{app,mnesia,[{incl_cond,include}]},
{app,sasl,[{incl_cond,include}]},
+ {app,inets,[{incl_cond,include}]},
{app,x,[{incl_cond,include}]},
{app,y,[{incl_cond,include}]},
{app,z,[{incl_cond,include}]},
@@ -324,7 +330,6 @@ create_release_sort(Config) ->
]},
%% Generate release
- %% BUG: reltool reverses the list of applications after kernel and stdlib
?msym({ok, {release, {RelName1, RelVsn},
{erts, _},
[{kernel, _},
@@ -333,7 +338,6 @@ create_release_sort(Config) ->
{sasl, _}]}},
reltool:get_rel([{config, Sys}], RelName1)),
- %% BUG: reltool reverses the list of applications after kernel and stdlib
?msym({ok, {release, {RelName2, RelVsn},
{erts, _},
[{kernel, _},
@@ -346,19 +350,21 @@ create_release_sort(Config) ->
{erts, _},
[{kernel, _},
{stdlib, _},
+ {sasl, _},
+ {inets, _},
{tools, _},
- {z, _, [tools]}]}},
+ {z, _, [tools]},
+ {mnesia, _}]}},
reltool:get_rel([{config, Sys}], RelName3)),
- %% BUG: reltool does not honor included applications in .app files
- %% unless they are also mentioned in the 'rel' specification in
- %% the reltool config.
- %% => order of tools and z does not become correct in rel (tools
- %% should be first since it is included in z)
+ %%! BUG: same as OTP-4121, but for reltool???? Or revert tools and mnesia
?msym({ok, {release, {RelName4, RelVsn},
{erts, _},
[{kernel, _},
{stdlib, _},
+ {sasl, _},
+ {inets, _},
+ {mnesia, _},
{tools, _},
{z, _}]}},
reltool:get_rel([{config, Sys}], RelName4)),
@@ -368,11 +374,7 @@ create_release_sort(Config) ->
"in the app file: [tools]"},
reltool:get_rel([{config, Sys}], RelName5)),
- %% BUG: reltool does not honor included applications in .app files
- %% unless they are also mentioned in the 'rel' specification in
- %% the reltool config.
- %% => does not detect that tools (included in z) is missing
- ?m({error, "Undefined applications: [tools]"},
+ ?m({error, "Undefined applications: [tools,mnesia]"},
reltool:get_rel([{config, Sys}], RelName6)),
?m({error,"Circular dependencies: [x,y]"},
@@ -381,15 +383,25 @@ create_release_sort(Config) ->
?m({error,"Undefined applications: [tools]"},
reltool:get_rel([{config, Sys}], RelName8)),
- %% BUG: Reltool looses the empty include list for z, which should
- %% overwrite included_applications statement from the .app file.
?msym({ok,{release,{RelName9,RelVsn},
{erts,_},
[{kernel,_},
{stdlib,_},
+ {sasl, _},
+ {inets, _},
{z,_,[]}]}},
reltool:get_rel([{config, Sys}], RelName9)),
+ %%! BUG: same as OTP-9984, but for reltool???? Or revert inets and sasl?
+ ?msym({ok,{release,{RelName10,RelVsn},
+ {erts,_},
+ [{kernel,_},
+ {stdlib,_},
+ {inets, _},
+ {sasl, _},
+ {z,_,[]}]}},
+ reltool:get_rel([{config, Sys}], RelName10)),
+
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -442,7 +454,7 @@ create_script(_Config) ->
%% Test creation of .script with different sorting of applications and
%% included applications.
%% Test that result is equal to what systools produces
-create_script_sort(_Config) -> {skip, "Multiple known problems - see OTP-9792"};
+create_script_sort(_Config) -> {skip, "OTP-9984 - stdlib sort problem"};
create_script_sort(Config) ->
DataDir = ?config(data_dir,Config),
%% Configure the server
@@ -464,10 +476,10 @@ create_script_sort(Config) ->
{boot_rel, RelName1},
{rel, RelName1, RelVsn, [stdlib, kernel, mnesia, sasl]},
{rel, RelName2, RelVsn, [stdlib, kernel, sasl, mnesia]},
- {rel, RelName3, RelVsn, [stdlib, kernel, {z,[tools]}, tools]},
- {rel, RelName4, RelVsn, [stdlib, kernel, z, tools]},
+ {rel, RelName3, RelVsn, [stdlib, kernel, {z,[tools]}, tools, mnesia]},
+ {rel, RelName4, RelVsn, [stdlib, kernel, z, mnesia, tools]},
{rel, RelName5, RelVsn, [stdlib, kernel, {sasl,[tools]}]},
- {rel, RelName6, RelVsn, [stdlib, kernel, z]}, %z includes tools in .app
+ {rel, RelName6, RelVsn, [stdlib, kernel, z]},
{rel, RelName7, RelVsn, [stdlib, kernel, mnesia, y, sasl, x]},
{rel, RelName8, RelVsn, [stdlib, kernel, {z,[tools]}]},
{rel, RelName9, RelVsn, [stdlib, kernel, {z,[]}]},
@@ -477,6 +489,7 @@ create_script_sort(Config) ->
{app,stdlib,[{incl_cond,include}]},
{app,mnesia,[{incl_cond,include}]},
{app,sasl,[{incl_cond,include}]},
+ {app,inets,[{incl_cond,include}]},
{app,x,[{incl_cond,include}]},
{app,y,[{incl_cond,include}]},
{app,z,[{incl_cond,include}]},
@@ -487,11 +500,13 @@ create_script_sort(Config) ->
%% Generate release files
application:load(sasl),
+ application:load(inets),
application:load(mnesia),
application:load(tools),
{ok,KernelVsn} = application:get_key(kernel,vsn),
{ok,StdlibVsn} = application:get_key(stdlib,vsn),
{ok,SaslVsn} = application:get_key(sasl,vsn),
+ {ok,InetsVsn} = application:get_key(inets,vsn),
{ok,MnesiaVsn} = application:get_key(mnesia,vsn),
{ok,ToolsVsn} = application:get_key(tools,vsn),
ErtsVsn = erlang:system_info(version),
@@ -514,14 +529,20 @@ create_script_sort(Config) ->
[{kernel,KernelVsn},
{stdlib,StdlibVsn},
{z,"1.0",[tools]},
- {tools,ToolsVsn}]},
+ {tools,ToolsVsn},
+ {mnesia,MnesiaVsn},
+ {sasl,SaslVsn},
+ {inets,InetsVsn}]},
FullName3 = filename:join(?WORK_DIR,RelName3),
?m(ok, file:write_file(FullName3 ++ ".rel", io_lib:format("~p.\n", [Rel3]))),
Rel4 = {release, {RelName4,RelVsn}, {erts,ErtsVsn},
[{kernel,KernelVsn},
{stdlib,StdlibVsn},
{z,"1.0"},
- {tools,ToolsVsn}]},
+ {tools,ToolsVsn},
+ {mnesia,MnesiaVsn},
+ {sasl,SaslVsn},
+ {inets,InetsVsn}]},
FullName4 = filename:join(?WORK_DIR,RelName4),
?m(ok, file:write_file(FullName4 ++ ".rel", io_lib:format("~p.\n", [Rel4]))),
Rel5 = {release, {RelName5,RelVsn}, {erts,ErtsVsn},
@@ -555,42 +576,30 @@ create_script_sort(Config) ->
Rel9 = {release, {RelName9,RelVsn}, {erts,ErtsVsn},
[{kernel,KernelVsn},
{stdlib,StdlibVsn},
- {z,"1.0",[]}]},
+ {z,"1.0",[]},
+ {sasl,SaslVsn},
+ {inets,InetsVsn}]},
FullName9 = filename:join(?WORK_DIR,RelName9),
?m(ok, file:write_file(FullName9 ++ ".rel", io_lib:format("~p.\n", [Rel9]))),
%% Generate script files with systools and reltool and compare
ZPath = filename:join([LibDir,"*",ebin]),
- %% BUG: reltool reverses the list of applications after kernel and stdlib
- %% => mnesia and sasl are reverted
?msym({ok,_,_}, systools_make_script(FullName1,ZPath)),
{ok, [SystoolsScript1]} = ?msym({ok,[_]}, file:consult(FullName1++".script")),
{ok, Script1} = ?msym({ok, _}, reltool:get_script(Pid, RelName1)),
?m(equal, diff_script(SystoolsScript1, Script1)),
- %% BUG: reltool reverses the list of applications after kernel and stdlib
- %% => mnesia and sasl are reverted
?msym({ok,_,_}, systools_make_script(FullName2,ZPath)),
{ok, [SystoolsScript2]} = ?msym({ok,[_]}, file:consult(FullName2++".script")),
{ok, Script2} = ?msym({ok, _}, reltool:get_script(Pid, RelName2)),
?m(equal, diff_script(SystoolsScript2, Script2)),
- %% BUG1: reltool loads all modules in the ebin dir of an application,
- %% even if mod_cond is set to 'app'.
- %% BUG2: reltool shall not start included applications!!
?msym({ok,_,_}, systools_make_script(FullName3,ZPath)),
{ok, [SystoolsScript3]} = ?msym({ok,[_]}, file:consult(FullName3++".script")),
{ok, Script3} = ?msym({ok, _}, reltool:get_script(Pid, RelName3)),
?m(equal, diff_script(SystoolsScript3, Script3)),
- %% BUG1: reltool loads all modules in the ebin dir of an application,
- %% even if mod_cond is set to 'app'.
- %% BUG2: reltool does not honor included applications in .app files
- %% unless they are also mentioned in the 'rel' specification in
- %% the reltool config.
- %% => faulty order of load instructions for tools and z. tools
- %% should be first since it is included in z.
?msym({ok,_,_}, systools_make_script(FullName4,ZPath)),
{ok, [SystoolsScript4]} = ?msym({ok,[_]}, file:consult(FullName4++".script")),
{ok, Script4} = ?msym({ok, _}, reltool:get_script(Pid, RelName4)),
@@ -603,13 +612,9 @@ create_script_sort(Config) ->
"in the app file: [tools]"},
reltool:get_script(Pid, RelName5)),
- %% BUG: reltool does not honor included applications in .app files
- %% unless they are also mentioned in the 'rel' specification in
- %% the reltool config.
- %% => does not detect that tools (included in z) is missing
?msym({error,_,{undefined_applications,_}},
systools_make_script(FullName6,ZPath)),
- ?m({error, "Undefined applications: [tools]"},
+ ?m({error, "Undefined applications: [tools,mnesia]"},
reltool:get_script(Pid, RelName6)),
?msym({error,_,{circular_dependencies,_}},
diff --git a/lib/reltool/test/reltool_server_SUITE_data/sort_apps/z-1.0/ebin/z.app b/lib/reltool/test/reltool_server_SUITE_data/sort_apps/z-1.0/ebin/z.app
index 1622975bf6..8608bc554b 100644
--- a/lib/reltool/test/reltool_server_SUITE_data/sort_apps/z-1.0/ebin/z.app
+++ b/lib/reltool/test/reltool_server_SUITE_data/sort_apps/z-1.0/ebin/z.app
@@ -4,5 +4,5 @@
{vsn, "1.0"},
{modules,[]},
{registered, []},
- {applications, [kernel, stdlib]},
- {included_applications, [tools]}]}.
+ {applications, [kernel, stdlib, sasl, inets]},
+ {included_applications, [tools, mnesia]}]}.