1#!/usr/bin/env escript
2%% -*- erlang -*-
3%%
4%% %CopyrightBegin%
5%%
6%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
7%%
8%% Licensed under the Apache License, Version 2.0 (the "License");
9%% you may not use this file except in compliance with the License.
10%% You may obtain a copy of the License at
11%%
12%%     http://www.apache.org/licenses/LICENSE-2.0
13%%
14%% Unless required by applicable law or agreed to in writing, software
15%% distributed under the License is distributed on an "AS IS" BASIS,
16%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17%% See the License for the specific language governing permissions and
18%% limitations under the License.
19%%
20%% %CopyrightEnd%
21
22-include_lib("reltool/src/reltool.hrl").
23
24main(Args) ->
25    process_flag(trap_exit, true),
26    try
27	Tokens = scan_args(Args, [], []),
28	{Options, Actions} = parse_args(Tokens, []),
29	case invoke(Options, Actions) of
30	    ok ->
31		safe_stop(0);
32	    {error, ReasonString} ->
33		fatal_error(ReasonString, 2)
34	end
35    catch
36	throw:usage ->
37	    usage(),
38	    safe_stop(1);
39	exit:Reason ->
40	    String = lists:flatten(io_lib:format("EXIT: ~p", [Reason])),
41	    fatal_error(String, 3)
42    end.
43
44usage() ->
45    Usage =
46	[
47	 "[Config] [--window]",
48	 "[Config] --create_config [-defaults] [-derived] [ConfigFile]",
49	 "[Config] --create_rel RelName [RelFile]",
50	 "[Config] --create_script RelName [ScriptFile]",
51	 "[Config] --create_target TargetDir",
52	 "[Config] --create_target_spec [SpecFile]",
53	 "[Config] --eval_target_spec Spec TargetDir RootDir"
54	],
55    Script = script_name(),
56    String = lists:flatten([[Script, " ", U, "\n"] || U <- Usage]),
57    io:format("Erlang/OTP release management tool\n\n"
58	      "~s\nConfig = ConfigFile | '{sys, [sys()]}'\n"
59	      "Spec   = SpecFile   | '{spec, [target_spec()}']\n\n"
60	      "See User's guide and Reference manual for more info.\n",
61	      [String]).
62
63safe_stop(Code) ->
64    init:stop(Code),
65    timer:sleep(infinity).
66
67invoke(Options, Actions) ->
68    case Actions of
69	[] ->
70	    invoke(Options, [["--window"]]);
71	[["--window"]] ->
72	    start_window(Options);
73	[["--create_config" | OptArgs]] ->
74	    DefArg = "-defaults",
75	    DerivArg = "-derived",
76	    InclDef = lists:member(DefArg, OptArgs),
77	    InclDeriv = lists:member(DerivArg, OptArgs),
78	    case reltool:get_config(Options, InclDef, InclDeriv) of
79		{ok, Config} ->
80		    String = pretty("config", Config),
81		    case OptArgs -- [DefArg, DerivArg] of
82			[] ->
83			    format("~s", [String]);
84			[ConfigFile] ->
85			    write_file(ConfigFile, String);
86			_ ->
87			    throw(usage)
88		    end;
89		{error, Reason} ->
90		    {error, Reason}
91	    end;
92	[["--create_rel", RelName | OptArgs]] ->
93	    case reltool:get_rel(Options, RelName) of
94		{ok, Rel} ->
95		    String = pretty("rel", Rel),
96		    case OptArgs of
97			[] ->
98			    format("~s", [String]);
99			[RelFile] ->
100			    write_file(RelFile, String);
101			_ ->
102			    throw(usage)
103		    end;
104		{error, Reason} ->
105		    {error, Reason}
106	    end;
107	[["--create_script", RelName | OptArgs]] ->
108	    case reltool:get_script(Options, RelName) of
109		{ok, Script} ->
110		    String = pretty("script", Script),
111		    case OptArgs of
112			[] ->
113			    format("~s", [String]);
114			[ScriptFile] ->
115			    write_file(ScriptFile, String);
116			_ ->
117			    throw(usage)
118		    end;
119		{error, Reason} ->
120		    {error, Reason}
121	    end;
122	[["--create_target", TargetDir]] ->
123	    reltool:create_target(Options, TargetDir);
124	[["--create_target_spec" | OptArgs]] ->
125	    case reltool:get_target_spec(Options) of
126		{ok, Script} ->
127		    String = pretty("target_spec", Script),
128		    case OptArgs of
129			[] ->
130			    format("~s", [String]);
131			[SpecFile] ->
132			    write_file(SpecFile, String);
133			_ ->
134			    throw(usage)
135		    end;
136		{error, Reason} ->
137		    {error, Reason}
138	    end;
139	[["--eval_target_spec", TargetSpec, TargetDir, RootDir]] ->
140	    try
141		{ok, Tokens, _} = erl_scan:string(TargetSpec ++ ". "),
142		{ok, {spec, Spec}} = erl_parse:parse_term(Tokens),
143		reltool:eval_target_spec(Spec, TargetDir, RootDir)
144	    catch
145		error:{badmatch, _} ->
146		    case file:consult(TargetSpec) of
147			{ok, Spec2} ->
148			    reltool:eval_target_spec(Spec2, TargetDir, RootDir);
149			{error, Reason} ->
150			    Text = file:format_error(Reason),
151			    {error, TargetSpec ++ ": " ++ Text}
152		    end
153	    end;
154	_ ->
155	    throw(usage)
156    end.
157
158start_window(Options) ->
159    case reltool:start_link(Options) of
160	{ok, WinPid} ->
161	    receive
162		{'EXIT', WinPid, shutdown} ->
163		    ok;
164		{'EXIT', WinPid, normal} ->
165		    ok;
166		{'EXIT', WinPid, Reason} ->
167		    exit(Reason)
168	    end;
169	{error, Reason} ->
170	    {error, Reason}
171    end.
172
173%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
174%% Helpers
175%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
176
177script_name() ->
178    filename:basename(escript:script_name(), ".escript").
179
180fatal_error(String, Code) ->
181    io:format(standard_error, "~s: ~s\n", [script_name(), String]),
182    safe_stop(Code).
183
184write_file(File, IoList) ->
185    case file:write_file(File, IoList) of
186	ok ->
187	    ok;
188	{error, Reason} ->
189	    {error, file:format_error(Reason)}
190    end.
191
192format(Format, Args) ->
193    io:format(Format, Args),
194    %% Wait a while for the I/O to be processed
195    timer:sleep(timer:seconds(1)).
196
197pretty(Tag, Term) ->
198    lists:flatten(io_lib:format("%% ~s generated at ~w ~w\n~p.\n\n",
199				[Tag, date(), time(), Term])).
200
201scan_args([H | T], Single, Multi) ->
202    case H of
203	"--" ++ _ when Single =:= [] ->
204	    scan_args(T, [H], Multi);
205	"--" ++ _ ->
206	    scan_args(T, [H], [lists:reverse(Single) | Multi]);
207	_ ->
208	    scan_args(T, [H | Single], Multi)
209    end;
210scan_args([], [], Multi) ->
211    lists:reverse(Multi);
212scan_args([], Single, Multi) ->
213    lists:reverse([lists:reverse(Single) | Multi]).
214
215parse_args([H | T] = Args, Options) ->
216    case H of
217	["--wx_debug" | Levels] ->
218	    Dbg =
219		fun(L) ->
220			case catch list_to_integer(L) of
221			    {'EXIT', _} ->
222				case catch list_to_atom(L) of
223				    {'EXIT', _} ->
224					exit("Illegal wx debug level: " ++ L);
225				    Atom ->
226						Atom
227				end;
228			    Int ->
229				Int
230			end
231		end,
232	    Levels2 = lists:map(Dbg, Levels),
233	    parse_args(T, [{wx_debug, Levels2} | Options]);
234	["--" ++ _ | _] ->
235	    %% No more options
236	    {lists:reverse(Options), Args};
237	[Config] ->
238	    try
239		{ok, Tokens, _} = erl_scan:string(Config ++ ". "),
240		{ok, {sys, _} = Sys} = erl_parse:parse_term(Tokens),
241		parse_args(T, [{config, Sys} | Options])
242	    catch
243		error:{badmatch, _} ->
244		    parse_args(T, [{config, Config} | Options]);
245		X:Y ->
246		    io:format("\n\n~p\n\n", [{X, Y}])
247	    end
248    end;
249parse_args([], Options) ->
250    {lists:reverse(Options), []}.
251