diff options
author | Håkan Mattsson <hm@tail-f.com> | 2013-06-24 15:09:28 +0200 |
---|---|---|
committer | Håkan Mattsson <hm@tail-f.com> | 2013-06-26 15:52:51 +0200 |
commit | af0c479f47c91fd1da1a0642b5176d0512e98b54 (patch) | |
tree | d52126d23e6ac6d0301be9848d3339f641e0a122 | |
parent | 10d93045982fc1477a4dade95d1cc400928fec14 (diff) | |
download | erlang-af0c479f47c91fd1da1a0642b5176d0512e98b54.tar.gz |
Publish reltool script
The script makes it simpler to use reltool from command line (and makefiles).
-rw-r--r-- | Makefile.in | 2 | ||||
-rw-r--r-- | erts/etc/unix/Install.src | 1 | ||||
-rw-r--r-- | erts/etc/win32/Install.c | 2 | ||||
-rwxr-xr-x | lib/reltool/bin/reltool | 289 | ||||
-rwxr-xr-x | lib/reltool/bin/reltool.escript | 249 | ||||
-rw-r--r-- | lib/reltool/doc/man1/.gitignore | 0 | ||||
-rw-r--r-- | lib/reltool/doc/src/Makefile | 21 | ||||
-rw-r--r-- | lib/reltool/doc/src/files.mk | 4 | ||||
-rw-r--r-- | lib/reltool/doc/src/reltool.xml | 4 | ||||
-rw-r--r-- | lib/reltool/doc/src/reltool_escript.xml | 98 | ||||
-rw-r--r-- | lib/reltool/src/Makefile | 5 | ||||
-rw-r--r-- | lib/reltool/src/reltool.hrl | 6 | ||||
-rw-r--r-- | lib/reltool/test/Makefile | 1 | ||||
-rw-r--r-- | lib/reltool/test/reltool_escript_SUITE.erl | 657 | ||||
-rw-r--r-- | lib/reltool/test/reltool_server_SUITE.erl | 1 | ||||
-rw-r--r-- | lib/reltool/vsn.mk | 2 |
16 files changed, 1074 insertions, 268 deletions
diff --git a/Makefile.in b/Makefile.in index d1fd64632c..85e0f3c89a 100644 --- a/Makefile.in +++ b/Makefile.in @@ -124,7 +124,7 @@ BINDIR = $(DESTDIR)$(EXTRA_PREFIX)$(bindir) # # Erlang base public files # -ERL_BASE_PUB_FILES=erl erlc epmd run_erl to_erl dialyzer typer escript ct_run +ERL_BASE_PUB_FILES=erl erlc epmd run_erl to_erl dialyzer typer escript reltool ct_run # ERLANG_INST_LIBDIR is the top directory where the Erlang installation # will be located when running. diff --git a/erts/etc/unix/Install.src b/erts/etc/unix/Install.src index 0f33258a28..420d2773ca 100644 --- a/erts/etc/unix/Install.src +++ b/erts/etc/unix/Install.src @@ -91,6 +91,7 @@ cp -p "$ERL_ROOT/erts-%I_VSN%/bin/dialyzer" . cp -p "$ERL_ROOT/erts-%I_VSN%/bin/typer" . cp -p "$ERL_ROOT/erts-%I_VSN%/bin/ct_run" . cp -p "$ERL_ROOT/erts-%I_VSN%/bin/escript" . +cp -p "reltool" "$ERL_ROOT/erts-%I_VSN%/bin/" # # Set a soft link to epmd diff --git a/erts/etc/win32/Install.c b/erts/etc/win32/Install.c index dd02a9c111..2ccd610bb6 100644 --- a/erts/etc/win32/Install.c +++ b/erts/etc/win32/Install.c @@ -46,7 +46,7 @@ int main(int argc, char **argv) HANDLE module = GetModuleHandle(NULL); char *binaries[] = { "erl.exe", "werl.exe", "erlc.exe", "dialyzer.exe", "typer.exe", - "escript.exe", "ct_run.exe", NULL }; + "escript.exe", "reltool", "ct_run.exe", NULL }; char *scripts[] = { "start_clean.boot", "start_sasl.boot", NULL }; char fromname[MAX_PATH]; char toname[MAX_PATH]; diff --git a/lib/reltool/bin/reltool b/lib/reltool/bin/reltool new file mode 100755 index 0000000000..589185b2ed --- /dev/null +++ b/lib/reltool/bin/reltool @@ -0,0 +1,289 @@ +#!/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, ReasonStr} -> + fatal_error(ReasonStr, 1) + end + catch + throw:usage -> + usage(), + safe_stop(1); + throw:help -> + usage(), + safe_stop(0); + throw:{error, ReasonStr2} -> + fatal_error(ReasonStr2, 1); + exit:Reason -> + ReasonStr2 = lists:flatten(io_lib:format("EXIT: ~p", [Reason])), + fatal_error(ReasonStr2, 2) + end. + +usage() -> + Usage = + [ + "[Config] --gui (start GUI)", + "[Config] --get_config [-defaults] [-derived] [File] (retrieve config info)", + "[Config] --get_rel RelName [File] (retrieve release info)", + "[Config] --get_script RelName [File] (retrieve boot script)", + "[Config] --create_target TargetDir (create target system)", + "[Config] --get_target_spec [File] (retrieve target spec)", + "--eval_target_spec Spec RootDir TargetDir (evaluate target spec)", + "--help (display this help)" + ], + Script = script_name(), + Signature = lists:flatten([[Script, " ", U, "\n"] || U <- Usage]), + io:format("Reltool is the release management tool of Erlang/OTP\n" + "\n" + "It analyses a given Erlang/OTP installation and determines\n" + "various dependencies between its applications. The graphical\n" + "frontend depicts the dependencies and enables interactive\n" + "customization of a target system. The backend provides a\n" + "batch interface for generation of customized target systems.\n" + "\n" + "~s" + "\n" + "Config = File | 'sys()'\n" + "File = filename()\n" + "RelName = rel_name()\n" + "TargetDir = target_dir()\n" + "Spec = File | 'target_spec()'\n" + "RootDir = root_dir()\n" + "\n" + "Exit status:\n" + " 0 - OK\n" + " 1 - error\n" + " 2 - internal error\n" + "\n" + "See the User's guide and Reference manual of reltool" + " for more info.\n", + [Signature]). + +safe_stop(Code) -> + init:stop(Code), + timer:sleep(infinity). + +invoke(Options, Actions) -> + case Actions of + [["--gui"]] -> + start_window(Options); + [["--get_config" | OptArgs]] -> + OptArgs2 = OptArgs -- ["-defaults"], + OptArgs3 = OptArgs2 -- ["-derived"], + InclDef = OptArgs2 =/= OptArgs, + InclDeriv = OptArgs3 =/= OptArgs2, + {ok, Config} = do_invoke(Options, reltool, get_config, + [InclDef, InclDeriv]), + String = pretty("config", Config), + case OptArgs3 of + [] -> + format("~s", [String]); + [ConfigFile] -> + write_file(ConfigFile, String); + _ -> + throw(usage) + end; + [["--get_rel", RelName | OptArgs]] -> + {ok, Rel} = do_invoke(Options, reltool, get_rel, [RelName]), + String = pretty("rel", Rel), + case OptArgs of + [] -> + format("~s", [String]); + [RelFile] -> + write_file(RelFile, String); + _ -> + throw(usage) + end; + [["--get_script", RelName | OptArgs]] -> + {ok, Script} = do_invoke(Options, reltool, get_script, [RelName]), + String = pretty("script", Script), + case OptArgs of + [] -> + format("~s", [String]); + [ScriptFile] -> + write_file(ScriptFile, String); + _ -> + throw(usage) + end; + [["--create_target", TargetDir]] -> + do_invoke(Options, reltool, create_target, [TargetDir]); + [["--get_target_spec" | OptArgs]] -> + {ok, Script} = do_invoke(Options, reltool, get_target_spec, []), + String = pretty("target_spec", Script), + case OptArgs of + [] -> + format("~s", [String]); + [SpecFile] -> + write_file(SpecFile, String); + _ -> + throw(usage) + end; + [["--eval_target_spec", TargetSpec0, RootDir, TargetDir]] -> + {spec, TargetSpec} = read_term(TargetSpec0, spec), + case reltool:eval_target_spec(TargetSpec, RootDir, TargetDir) of + ok -> + ok; + {error, ReasonStr} -> + throw({error, ReasonStr}) + end; + _ -> + throw(usage) + end. + +do_invoke(Options, M, F, A) -> + case reltool:start_server(Options) of + {ok, ServerPid} -> + case reltool:get_status(ServerPid) of + {ok, Warnings} -> + [io:format(standard_error, + "WARNING: ~s\n", [W]) || W <- Warnings], + Res = apply(M, F, [ServerPid | A]), + _StopRes = reltool:stop(ServerPid), + case Res of + {error, ReasonStr} -> + throw({error, ReasonStr}); + _ -> + Res + end; + {error, ReasonStr} -> + throw({error, ReasonStr}) + end; + {error, ReasonStr} -> + throw({error, ReasonStr}) + 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, ReasonStr} -> + throw({error, ReasonStr}) + end. + +read_term("-"++_, _Tag) -> + throw(usage); +read_term(String, Tag) -> + case file:consult(String) of + {ok, [{Tag, _} = Term]} -> + Term; + {ok, Terms} -> + ReasonStr = lists:flatten(io_lib:format("Illegal ~p: ~p", + [Tag, Terms])), + throw({error, ReasonStr}); + {error, FileReason} -> + try + {ok, Tokens, _} = erl_scan:string(String ++ ". "), + {ok, {Tag, _} = Term} = erl_parse:parse_term(Tokens), + Term + catch + error:{badmatch, _} -> + Text = file:format_error(FileReason), + throw({error, String ++ ": " ++ Text}) + end + 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 + ["--help"] -> + throw(help); + ["--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}; + [Config0] -> + Sys = read_term(Config0, sys), + parse_args(T, [{config, Sys} | Options]) + end; +parse_args([], Options) -> + {lists:reverse(Options), []}. diff --git a/lib/reltool/bin/reltool.escript b/lib/reltool/bin/reltool.escript deleted file mode 100755 index 0dcd5ad1e9..0000000000 --- a/lib/reltool/bin/reltool.escript +++ /dev/null @@ -1,249 +0,0 @@ -#!/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/man1/.gitignore b/lib/reltool/doc/man1/.gitignore new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/lib/reltool/doc/man1/.gitignore diff --git a/lib/reltool/doc/src/Makefile b/lib/reltool/doc/src/Makefile index 2373b5e507..ab65303978 100644 --- a/lib/reltool/doc/src/Makefile +++ b/lib/reltool/doc/src/Makefile @@ -40,7 +40,8 @@ include files.mk # ---------------------------------------------------- XML_FILES = \ - $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) \ + $(BOOK_FILES) $(XML_APPLICATION_FILES) \ + $(XML_REF1_FILES) $(XML_REF3_FILES) \ $(XML_PART_FILES) $(XML_CHAPTER_FILES) HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \ @@ -48,17 +49,17 @@ HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \ INFO_FILE = ../../info +MAN1_FILES = $(XML_REF1_FILES:%.xml=$(MAN1DIR)/%.1) MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3) HTML_REF_MAN_FILE = $(HTMLDIR)/index.html TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf - # ---------------------------------------------------- -# FLAGS +# FLAGS # ---------------------------------------------------- -XML_FLAGS += +XML_FLAGS += # ---------------------------------------------------- # Targets @@ -74,11 +75,12 @@ pdf: $(TOP_PDF_FILE) html:images $(HTML_REF_MAN_FILE) -man: $(MAN3_FILES) +man: $(MAN1_FILES) $(MAN3_FILES) + mv $(MAN1DIR)/reltool_escript.1 $(MAN1DIR)/reltool.1 images: $(IMAGE_FILES:%=$(HTMLDIR)/%) -debug opt: +debug opt: clean clean_docs: for file in $(XML_FILES); do \ @@ -87,13 +89,13 @@ clean clean_docs: fi \ done rm -rf $(HTMLDIR)/* - rm -f $(MAN3DIR)/* + rm -f $(MAN3DIR)/* $(MAN1DIR)/* rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) rm -f errs core *~ # ---------------------------------------------------- # Release Target -# ---------------------------------------------------- +# ---------------------------------------------------- include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs @@ -103,7 +105,8 @@ release_docs_spec: docs $(INSTALL_DATA) $(HTMLDIR)/* "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN1DIR)/* "$(RELEASE_PATH)/man/man1" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man1" $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" release_spec: - diff --git a/lib/reltool/doc/src/files.mk b/lib/reltool/doc/src/files.mk index 07b52f4934..265b905456 100644 --- a/lib/reltool/doc/src/files.mk +++ b/lib/reltool/doc/src/files.mk @@ -23,6 +23,9 @@ XML_APPLICATION_FILES = \ XML_REF3_FILES = \ reltool.xml +XML_REF1_FILES = \ + reltool_escript.xml + XML_PART_FILES = \ part.xml @@ -35,4 +38,3 @@ XML_CHAPTER_FILES = \ BOOK_FILES = book.xml IMAGE_FILES = - diff --git a/lib/reltool/doc/src/reltool.xml b/lib/reltool/doc/src/reltool.xml index 8437b7a623..a5b5c47d8b 100644 --- a/lib/reltool/doc/src/reltool.xml +++ b/lib/reltool/doc/src/reltool.xml @@ -39,7 +39,7 @@ <p><em>Reltool</em> is a release management tool. It analyses a given Erlang/OTP installation and determines various dependencies - between applications. The <c>graphical</c> frontend depicts the + between its applications. The <c>graphical</c> frontend depicts the dependencies and enables interactive customization of a target system. The backend provides a <c>batch</c> interface for generation of customized target systems.</p> @@ -697,7 +697,7 @@ target_spec() = [target_spec()] </func> <func> - <name>get_script(Server, Relname) -> {ok, ScriptFile | {error, Reason}</name> + <name>get_script(Server, Relname) -> {ok, ScriptFile} | {error, Reason}</name> <fsummary>Get contents of a boot script file</fsummary> <type> <v>Server = server()</v> diff --git a/lib/reltool/doc/src/reltool_escript.xml b/lib/reltool/doc/src/reltool_escript.xml new file mode 100644 index 0000000000..507f5ef125 --- /dev/null +++ b/lib/reltool/doc/src/reltool_escript.xml @@ -0,0 +1,98 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE comref SYSTEM "comref.dtd"> + +<comref> + <header> + <copyright> + <year>2013</year> + <year>2013</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + 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. + + </legalnotice> + + <title>erl</title> + <prepared></prepared> + <docno></docno> + <date></date> + <rev></rev> + <file>reltool_escript.xml</file> + </header> + <com>reltool</com> + <comsummary>Erlang/OTP release management tool</comsummary> + <description> + <p><em>Reltool</em> is a release management tool. It analyses a + given Erlang/OTP installation and determines various dependencies + between its applications. The <c>graphical</c> frontend depicts the + dependencies and enables interactive customization of a target + system. The backend provides a <c>batch</c> interface for + generation of customized target systems.</p> + </description> + <funcs> + <func> + <name>reltool --gui></name> + <fsummary>Start GUI</fsummary> + <desc> + <p>Starts the graphical user interface. See <em>reltool:start/1</em>.</p> + </desc> + </func> + <func> + <name>reltool [Config] --get_config [-defaults] [-derived] [File]></name> + <fsummary>Retrieves actual config</fsummary> + <desc> + <p>See <em>reltool:get_config/3</em>.</p> + </desc> + </func> + <func> + <name>reltool [Config] --get_rel RelName [File]></name> + <fsummary>Retrieves actual release info</fsummary> + <desc> + <p>See <em>reltool:get_rel/2</em>.</p> + </desc> + </func> + <func> + <name>reltool [Config] --get_script RelName [File]></name> + <fsummary>Retrieves actual boot script</fsummary> + <desc> + <p>See <em>reltool:get_script/2</em>.</p> + </desc> + </func> + <func> + <name>reltool [Config] --create_target TargetDir></name> + <fsummary>Generate a target system</fsummary> + <desc> + <p>See <em>reltool:create_target/2</em>.</p> + </desc> + </func> + <func> + <name>reltool [Config] --get_target_spec [SpecFile]</name> + <fsummary>Retrieve spec of target system</fsummary> + <desc> + <p>See <em>reltool:get_target_spec/1</em>.</p> + </desc> + </func> + <func> + <name>reltool --eval_target_spec Spec RootDir TargetDir</name> + <fsummary>Generate target system from spec</fsummary> + <desc> + <p>See <em>reltool:eval_target_spec/3</em>.</p> + </desc> + </func> + </funcs> + + <section> + <title>SEE ALSO</title> + <p><seealso marker="reltool">reltool(3)</seealso></p> + </section> +</comref> diff --git a/lib/reltool/src/Makefile b/lib/reltool/src/Makefile index b8387fff96..b95b75f45b 100644 --- a/lib/reltool/src/Makefile +++ b/lib/reltool/src/Makefile @@ -52,6 +52,8 @@ APPUP_FILE = reltool.appup APPUP_SRC = $(APPUP_FILE).src APPUP_TARGET = $(EBIN)/$(APPUP_FILE) +ESCRIPT_FILE = reltool + # ---------------------------------------------------- # FLAGS # ---------------------------------------------------- @@ -103,6 +105,7 @@ release_spec: opt $(INSTALL_DIR) "$(RELSYSDIR)/ebin" $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" $(INSTALL_DATA) $(APP_TARGET) $(APPUP_TARGET) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELEASE_PATH)/bin" + $(INSTALL_PROGRAM) ../bin/$(ESCRIPT_FILE) "$(RELEASE_PATH)/bin/$(ESCRIPT_FILE)" release_docs_spec: - diff --git a/lib/reltool/src/reltool.hrl b/lib/reltool/src/reltool.hrl index f0d8b38519..f38a732b0f 100644 --- a/lib/reltool/src/reltool.hrl +++ b/lib/reltool/src/reltool.hrl @@ -287,8 +287,8 @@ "^lib", "^releases"]). -define(EMBEDDED_EXCL_SYS_FILTERS, - ["^bin/(erlc|dialyzer|typer)(|\\.exe)\$", - "^erts.*/bin/(erlc|dialyzer|typer)(|\\.exe)\$", + ["^bin/(erlc|dialyzer|reltool|typer)(|\\.exe)\$", + "^erts.*/bin/(erlc|dialyzer|reltool|typer)(|\\.exe)\$", "^erts.*/bin/.*(debug|pdb)"]). -define(EMBEDDED_INCL_APP_FILTERS, ["^ebin", "^include", @@ -301,7 +301,7 @@ "^erts.*/bin", "^lib\$"]). -define(STANDALONE_EXCL_SYS_FILTERS, - ["^erts.*/bin/(erlc|dialyzer|typer)(|\\.exe)\$", + ["^erts.*/bin/(erlc|dialyzer|reltool|typer)(|\\.exe)\$", "^erts.*/bin/(start|escript|to_erl|run_erl)(|\\.exe)\$", "^erts.*/bin/.*(debug|pdb)"]). -define(STANDALONE_INCL_APP_FILTERS, ["^ebin", diff --git a/lib/reltool/test/Makefile b/lib/reltool/test/Makefile index 52cdef44da..caf36ff991 100644 --- a/lib/reltool/test/Makefile +++ b/lib/reltool/test/Makefile @@ -28,6 +28,7 @@ MODULES= \ reltool_app_SUITE \ reltool_wx_SUITE \ reltool_server_SUITE \ + reltool_escript_SUITE \ reltool_manual_gui_SUITE \ reltool_test_lib diff --git a/lib/reltool/test/reltool_escript_SUITE.erl b/lib/reltool/test/reltool_escript_SUITE.erl new file mode 100644 index 0000000000..0e2685cd93 --- /dev/null +++ b/lib/reltool/test/reltool_escript_SUITE.erl @@ -0,0 +1,657 @@ +%% -*- coding: utf-8 -*- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2013. 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_escript_SUITE). + +-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2, + init_per_suite/1, end_per_suite/1, + init_per_testcase/2, end_per_testcase/2]). + +-compile(export_all). + +-include_lib("reltool/src/reltool.hrl"). +-include("reltool_test_lib.hrl"). +-include_lib("common_test/include/ct.hrl"). +-include_lib("kernel/include/file.hrl"). + +-define(NODE_NAME, '__RELTOOL__TEMPORARY_TEST__NODE__'). +-define(WORK_DIR, "reltool_work_dir"). +-define(TMP_IN_FILE, "tmp_reltool_infile"). +-define(TMP_OUT_FILE, "tmp_reltool_outfile"). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Initialization functions. + +init_per_suite(Config) -> + {ok,Cwd} = file:get_cwd(), + ?ignore(file:make_dir(?WORK_DIR)), + [{cwd,Cwd}|reltool_test_lib:init_per_suite(Config)]. + +end_per_suite(Config) -> + reltool_test_lib:end_per_suite(Config). + +init_per_testcase(Func,Config) -> + Node = full_node_name(?NODE_NAME), + case net_adm:ping(Node) of + pong -> stop_node(Node); + pang -> ok + end, + reltool_test_lib:init_per_testcase(Func,Config). + +end_per_testcase(Func,Config) -> + ok = file:set_cwd(filename:join(?config(cwd,Config),?WORK_DIR)), + {ok,All} = file:list_dir("."), + Files = [F || F <- All, false =:= lists:prefix("save.",F)], + case ?config(tc_status,Config) of + ok -> + ok; + _Fail -> + SaveDir = "save."++atom_to_list(Func), + ok = rm_files([SaveDir]), + ok = file:make_dir(SaveDir), + save_test_result(Files,SaveDir) + end, + rm_files(Files), + ok = file:set_cwd(?config(cwd,Config)), + rm_files([?TMP_IN_FILE, ?TMP_OUT_FILE]), + reltool_test_lib:end_per_testcase(Func,Config). + +save_test_result(Files,DestDir) -> + Tar = "copy.tar", + ok = erl_tar:create(Tar, Files), + ok = erl_tar:extract(Tar, [{cwd,DestDir}]), + ok = file:delete(Tar), + ok. + +rm_files([F | Fs]) -> + case file:read_file_info(F) of + {ok,#file_info{type=directory}} -> + rm_dir(F); + {ok,_Regular} -> + ok = file:delete(F); + {error, enoent} -> + ok + end, + rm_files(Fs); +rm_files([]) -> + ok. + +rm_dir(Dir) -> + {ok,Files} = file:list_dir(Dir), + rm_files([filename:join(Dir, F) || F <- Files]), + ok = file:del_dir(Dir). + +oscmd(CmdStr) -> + io:format("os:cmd(~p)\n", [CmdStr]), + Output0 = os:cmd(CmdStr ++ "; echo \";$?\""), + Pred = fun(Char) -> Char =/= $; end, + {ExitStatus, [$; | RevOutput]} = + lists:splitwith(Pred, lists:reverse(Output0)), + Output = lists:reverse(RevOutput), + ExitStatus2 = string:strip(ExitStatus, left, $\n), + {list_to_integer(ExitStatus2), Output}. + +-define(w,'%.*%'). +-define(f(Term), lists:flatten(io_lib:format("~p", [Term]))). + +reltool_plain(Cmd) -> + file:delete(?TMP_OUT_FILE), + {ExitCode, Output} = oscmd(Cmd), + file:write_file(?TMP_OUT_FILE, Output), + case file:consult(?TMP_OUT_FILE) of + {ok, Term} -> + file:delete(?TMP_OUT_FILE), + {ExitCode, {ok, Term}}; + {error, _Reason} -> + file:delete(?TMP_OUT_FILE), + {ExitCode, {error, Output}} + end. + +reltool(Cmd) -> + Plain = reltool_plain(Cmd), + File = reltool_file(Cmd), + ?m(Plain, File). + +reltool_file(Cmd) -> + file:delete(?TMP_OUT_FILE), + {ExitCode, Output} = oscmd(Cmd ++ " " ++ ?TMP_OUT_FILE), + case file:consult(?TMP_OUT_FILE) of + {ok, Term} -> + file:delete(?TMP_OUT_FILE), + {ExitCode, {ok, Term}}; + {error, _Reason} -> + file:delete(?TMP_OUT_FILE), + {ExitCode, {error, _Reason, Output}} + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% SUITE specification + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [start_gui, + get_config, + create_release, + create_script, + create_target, + eval_target_spec + ]. + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +%% The test cases + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% A dummy break test case which is NOT in all(), but can be run +%% directly from the command line with ct_run. It just does a +%% test_server:break()... +break(_Config) -> + test_server:break(""), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Start the GUI + +start_gui(_Config) -> + ?m({1, {error, "reltool: noSUCHfile: no such file or directory\n"}}, + reltool_plain("reltool noSUCHfile --gui")), + %% ?m({0,""}, oscmd("reltool --gui")), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Check that get_config returns the expected derivates and defaults +%% as specified +get_config(_Config) -> + KernVsn = latest(kernel), + StdVsn = latest(stdlib), + SaslVsn = latest(sasl), + LibDir = code:lib_dir(), + StdLibDir = filename:join(LibDir,"stdlib-"++StdVsn), + KernLibDir = filename:join(LibDir,"kernel-"++KernVsn), + SaslLibDir = filename:join(LibDir,"sasl-"++SaslVsn), + + ?m({0, {ok, [{sys,[]}]}}, + reltool("reltool --get_config")), + + Sys = {sys,[{incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include},{vsn,SaslVsn}]}, + {app,stdlib,[{incl_cond,include},{lib_dir,StdLibDir}]}]}, + ?m(ok, file:write_file(?TMP_IN_FILE, ?f(Sys) ++ ".")), + + ?m({0, {ok, [Sys]}}, + reltool("reltool " ++ ?TMP_IN_FILE ++ " --get_config")), + + %% Include derived info + ?msym({0, + {ok, [{sys,[{incl_cond, exclude}, + {erts,[]}, + {app,kernel,[{incl_cond,include},{mod,_,[]}|_]}, + {app,sasl,[{incl_cond,include},{vsn,SaslVsn},{mod,_,[]}|_]}, + {app,stdlib,[{incl_cond,include},{lib_dir,StdLibDir}, + {mod,_,[]}|_]}]}]}}, + reltool("reltool " ++ ?TMP_IN_FILE ++ " --get_config -derived")), + + %% Include defaults + ?msym({0, + {ok, [{sys,[{root_dir,_}, + {lib_dirs,_}, + {mod_cond,all}, + {incl_cond,exclude}, + {app,kernel,[{incl_cond,include},{vsn,undefined}, + {lib_dir,undefined}]}, + {app,sasl,[{incl_cond,include},{vsn,SaslVsn}, + {lib_dir,undefined}]}, + {app,stdlib,[{incl_cond,include},{vsn,undefined}, + {lib_dir,StdLibDir}]}, + {boot_rel,"start_clean"}, + {rel,"start_clean","1.0",[]}, + {rel,"start_sasl","1.0",[sasl]}, + {emu_name,"beam"}, + {relocatable,true}, + {profile,development}, + {incl_sys_filters,[".*"]}, + {excl_sys_filters,[]}, + {incl_app_filters,[".*"]}, + {excl_app_filters,[]}, + {incl_archive_filters,[".*"]}, + {excl_archive_filters,["^include$","^priv$"]}, + {archive_opts,[]}, + {rel_app_type,permanent}, + {app_file,keep}, + {debug_info,keep}]}]}}, + reltool("reltool " ++ ?TMP_IN_FILE ++ " --get_config -defaults")), + + %% Include both defaults and derived info + ?msym({0, + {ok, [{sys,[{root_dir,_}, + {lib_dirs,_}, + {mod_cond,all}, + {incl_cond,exclude}, + {erts,[]}, + {app,kernel,[{incl_cond,include},{vsn,KernVsn}, + {lib_dir,KernLibDir},{mod,_,[]}|_]}, + {app,sasl,[{incl_cond,include},{vsn,SaslVsn}, + {lib_dir,SaslLibDir},{mod,_,[]}|_]}, + {app,stdlib,[{incl_cond,include},{vsn,StdVsn}, + {lib_dir,StdLibDir},{mod,_,[]}|_]}, + {boot_rel,"start_clean"}, + {rel,"start_clean","1.0",[]}, + {rel,"start_sasl","1.0",[sasl]}, + {emu_name,"beam"}, + {relocatable,true}, + {profile,development}, + {incl_sys_filters,[".*"]}, + {excl_sys_filters,[]}, + {incl_app_filters,[".*"]}, + {excl_app_filters,[]}, + {incl_archive_filters,[".*"]}, + {excl_archive_filters,["^include$","^priv$"]}, + {archive_opts,[]}, + {rel_app_type,permanent}, + {app_file,keep}, + {debug_info,keep}]}]}}, + reltool("reltool " ++ ?TMP_IN_FILE ++ + " --get_config -derived -defaults")), + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Generate releases + +create_release(_Config) -> + %% Configure the server + RelName = "Testing", + RelVsn = "1.0", + Sys = + {sys, + [ + {lib_dirs, []}, + {boot_rel, RelName}, + {rel, RelName, RelVsn, [kernel, stdlib]} + ]}, + ?m(ok, file:write_file(?TMP_IN_FILE, ?f(Sys) ++ ".")), + + %% Generate release + ErtsVsn = erlang:system_info(version), + Apps = application:loaded_applications(), + {value, {_, _, KernelVsn}} = lists:keysearch(kernel, 1, Apps), + {value, {_, _, StdlibVsn}} = lists:keysearch(stdlib, 1, Apps), + Rel = + {release, {RelName, RelVsn}, + {erts, ErtsVsn}, + [{kernel, KernelVsn}, {stdlib, StdlibVsn}]}, + ?m({0, {ok,[Rel]}}, + reltool("reltool " ++ ?TMP_IN_FILE ++ + " --get_rel " ++ RelName)), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Generate boot scripts + +create_script(_Config) -> + %% Configure the server + RelName = "TestingMore", + RelVsn = "1.0", + Sys = + {sys, + [ + {lib_dirs, []}, + {boot_rel, RelName}, + {rel, RelName, RelVsn, [stdlib, kernel]} + ]}, + ?m(ok, file:write_file(?TMP_IN_FILE, ?f(Sys) ++ ".")), + + %% Generate release file + ErtsVsn = erlang:system_info(version), + Apps = application:loaded_applications(), + {value, {_, _, KernelVsn}} = lists:keysearch(kernel, 1, Apps), + {value, {_, _, StdlibVsn}} = lists:keysearch(stdlib, 1, Apps), + Rel = {release, + {RelName, RelVsn}, + {erts, ErtsVsn}, + [{kernel, KernelVsn}, {stdlib, StdlibVsn}]}, + ?m({0,{ok, [Rel]}}, + reltool("reltool " ++ ?TMP_IN_FILE ++ + " --get_rel " ++ RelName)), + + ?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, [])), + {ok, [OrigScript]} = ?msym({ok, [_]}, file:consult(RelName ++ ".script")), + ?m(ok, file:set_cwd(Cwd)), + + {_, {ok, [Script]}} = + ?msym({0, {ok, [_]}}, + reltool("reltool " ++ ?TMP_IN_FILE ++ + " --get_script " ++ RelName)), + + %% OrigScript2 = sort_script(OrigScript), + %% Script2 = sort_script(Script), + %% ?m(OrigScript2, Script2), + + ?m(equal, diff_script(OrigScript, Script)), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Generate target system + +create_target(_Config) -> + %% Configure the server + RelName1 = "Testing1", + RelName2 = "Testing2", + RelVsn = "1.0", + Sys = + {sys, + [ + {root_dir, code:root_dir()}, + {lib_dirs, []}, + {boot_rel, RelName2}, + {rel, RelName1, RelVsn, [stdlib, kernel]}, + {rel, RelName2, RelVsn, [sasl, stdlib, kernel]}, + {app, sasl, [{incl_cond, include}]} + ]}, + ?m(ok, file:write_file(?TMP_IN_FILE, ?f(Sys) ++ ".")), + + %% Generate target file + TargetDir = filename:join([?WORK_DIR, "target_development"]), + ?m(ok, reltool_utils:recursive_delete(TargetDir)), + ?m(ok, file:make_dir(TargetDir)), + + ?log("SPEC: ~p\n", + [?msym({0, {ok,[_]}}, + reltool_plain("reltool " ++ ?TMP_IN_FILE ++ + " --get_target_spec"))]), + + ?msym({0, {ok,[_]}}, + reltool_file("reltool " ++ ?TMP_IN_FILE ++ " --get_target_spec")), + + ?msym({0, {ok,[]}}, + reltool_plain("reltool " ++ ?TMP_IN_FILE ++ + " --create_target " ++ TargetDir)), + + BinDir = filename:join([TargetDir, "bin"]), + Erl = filename:join([BinDir, "erl"]), + {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), + ?msym(ok, stop_node(Node)), + + %% Verify that reltool is included + Reltool = filename:join([BinDir, "reltool"]), + ?m(Reltool, os:find_executable("reltool", BinDir)), + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Generate target system with eval_target_spec/3 + +eval_target_spec(_Config) -> + %% Configure the server + RelName1 = "Testing3", + RelName2 = "Testing4", + RelVsn = "1.0", + Sys = + {sys, + [ + {root_dir, code:root_dir()}, + {lib_dirs, []}, + {boot_rel, RelName2}, + {rel, RelName1, RelVsn, [stdlib, kernel]}, + {rel, RelName2, RelVsn, [sasl, stdlib, kernel]}, + {app, sasl, [{incl_cond, include}]} + ]}, + ?m(ok, file:write_file(?TMP_IN_FILE, ?f(Sys) ++ ".")), + + %% Generate target file + TargetDir = filename:join([?WORK_DIR, "eval_target_spec"]), + ?m(ok, reltool_utils:recursive_delete(TargetDir)), + ?m(ok, file:make_dir(TargetDir)), + + {_, {ok, Spec}} = + ?msym({0, {ok,_}}, + reltool_plain("reltool " ++ ?TMP_IN_FILE ++ + " --get_target_spec")), + ?msym({0, {ok,_}}, + reltool_file("reltool " ++ ?TMP_IN_FILE ++ " --get_target_spec")), + + ?m(ok, file:write_file(?TMP_IN_FILE, ?f({spec, Spec}) ++ ".")), + + ?msym({0, {ok,""}}, + reltool_plain("reltool --eval_target_spec " ++ " " ++ + ?TMP_IN_FILE ++ " " ++ + code:root_dir() ++ " " ++ + TargetDir)), + + BinDir = filename:join([TargetDir, "bin"]), + Erl = filename:join([BinDir, "erl"]), + {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), + ?msym(ok, stop_node(Node)), + + %% Verify that reltool is included + Reltool = filename:join([BinDir, "reltool"]), + ?m(Reltool, os:find_executable("reltool", BinDir)), + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Library functions + +erl_libs() -> + case os:getenv("ERL_LIBS") of + false -> []; + LibStr -> string:tokens(LibStr, ":;") + end. + +datadir(Config) -> + %% Removes the trailing slash... + filename:nativename(?config(data_dir,Config)). + +latest(App) -> + AppStr = atom_to_list(App), + AppDirs = filelib:wildcard(filename:join(code:lib_dir(),AppStr++"-*")), + [LatestAppDir|_] = lists:reverse(AppDirs), + [_,Vsn] = string:tokens(filename:basename(LatestAppDir),"-"), + Vsn. + +rm_missing_app(Apps) -> + lists:keydelete(?MISSING_APP_NAME,#app.name,Apps). + +diff_script(Script, Script) -> + equal; +diff_script({script, Rel, Commands1}, {script, Rel, Commands2}) -> + diff_cmds(Commands1, Commands2); +diff_script({script, Rel1, _}, {script, Rel2, _}) -> + {error, {Rel1, Rel2}}. + +diff_cmds([Cmd | Commands1], [Cmd | Commands2]) -> + diff_cmds(Commands1, Commands2); +diff_cmds([Cmd1 | _Commands1], [Cmd2 | _Commands2]) -> + {diff, {expected, Cmd1}, {actual, Cmd2}}; +diff_cmds([], []) -> + equal. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Node handling + +start_node(Name, ErlPath) -> + start_node(Name, ErlPath, []). +start_node(Name, ErlPath, Args0) -> + FullName = full_node_name(Name), + Args = mk_node_args(Name, Args0), + io:format("Starting node ~p: ~ts~n", + [FullName, lists:flatten([[X," "] || X <- [ErlPath|Args]])]), + %% io:format("open_port({spawn_executable, ~p}, + %% [{args,~p}])~n",[ErlPath,Args]), + case open_port({spawn_executable, ErlPath}, [{args,Args}]) of + Port when is_port(Port) -> + %% no need to close port since node is detached (see + %% mk_node_args) so port will be closed anyway. + case ping_node(FullName, 50) of + ok -> {ok, FullName}; + Other -> exit({failed_to_start_node, FullName, Other}) + end; + Error -> + exit({failed_to_start_node, FullName, Error}) + end. + +stop_node(Node) -> + rpc:call(Node,erlang,halt,[]), + wait_for_node_down(Node,50). + +wait_for_node_down(Node,0) -> + test_server:fail({cant_terminate_node,Node}); +wait_for_node_down(Node,N) -> + case net_adm:ping(Node) of + pong -> + timer:sleep(1000), + wait_for_node_down(Node,N-1); + pang -> + ok + end. + +mk_node_args(Name, Args) -> + Pa = filename:dirname(code:which(?MODULE)), + NameSw = case net_kernel:longnames() of + false -> "-sname"; + true -> "-name"; + _ -> exit(not_distributed_node) + end, + {ok, Pwd} = file:get_cwd(), + NameStr = atom_to_list(Name), + ["-detached", + NameSw, NameStr, + "-pa", Pa, + "-env", "ERL_CRASH_DUMP", Pwd ++ "/erl_crash_dump." ++ NameStr, + "-setcookie", atom_to_list(erlang:get_cookie()) + | Args]. + +full_node_name(PreName) -> + HostSuffix = lists:dropwhile(fun ($@) -> false; (_) -> true end, + atom_to_list(node())), + list_to_atom(atom_to_list(PreName) ++ HostSuffix). + +ping_node(_Node, 0) -> + {error, net_adm}; +ping_node(Node, N) when is_integer(N), N > 0 -> + case catch net_adm:ping(Node) of + pong -> + wait_for_process(Node, code_server, 50); + _ -> + timer:sleep(1000), + ping_node(Node, N-1) + end. + +wait_for_process(_Node, Name, 0) -> + {error, Name}; +wait_for_process(Node, Name, N) when is_integer(N), N > 0 -> + case rpc:call(Node, erlang, whereis, [Name]) of + undefined -> + timer:sleep(1000), + wait_for_process(Node, Name, N-1); + {badrpc, _} = Reason -> + erlang:error({Reason, Node}); + Pid when is_pid(Pid) -> + ok + end. + +wait_for_app(_Node, Name, 0) -> + {error, Name}; +wait_for_app(Node, Name, N) when is_integer(N), N > 0 -> + case rpc:call(Node,application,which_applications,[]) of + {badrpc,Reason} -> + test_server:fail({failed_to_get_applications,Reason}); + Apps -> + case lists:member(Name,Apps) of + false -> + timer:sleep(1000), + wait_for_app(Node, Name, N-1); + true -> + ok + end + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Run escript + +run(Dir, Script, Args) -> + Cmd0 = filename:rootname(Script) ++ " " ++ Args, + Cmd = case os:type() of + {win32,_} -> filename:nativename(Dir) ++ "\\" ++ Cmd0; + _ -> Cmd0 + end, + do_run(Dir, Cmd). + +run(Dir, Opts, Script, Args) -> + Cmd0 = filename:rootname(Script) ++ " " ++ Args, + Cmd = case os:type() of + {win32,_} -> Opts ++ " " ++ filename:nativename(Dir) ++ "\\" ++ Cmd0; + _ -> Opts ++ " " ++ Dir ++ "/" ++ Cmd0 + end, + do_run(Dir, Cmd). + +do_run(Dir, Cmd) -> + io:format("Run: ~p\n", [Cmd]), + Env = [{"PATH",Dir++":"++os:getenv("PATH")}, + {"ERL_FLAGS",""}, % Make sure no flags are set that can override + {"ERL_ZFLAGS",""}], % any of the flags set in the escript. + Port = open_port({spawn,Cmd}, [exit_status,eof,in,{env,Env}]), + Res = get_data(Port, []), + receive + {Port,{exit_status,ExitCode}} -> + s2b([Res,"ExitCode:"++integer_to_list(ExitCode)]) + end. + +get_data(Port, SoFar) -> + receive + {Port,{data,Bytes}} -> + get_data(Port, [SoFar|Bytes]); + {Port,eof} -> + erlang:port_close(Port), + SoFar + end. + +expected_output([data_dir|T], Data) -> + Slash = case os:type() of + {win32,_} -> "\\"; + _ -> "/" + end, + [filename:nativename(Data)++Slash|expected_output(T, Data)]; +expected_output([H|T], Data) -> + [H|expected_output(T, Data)]; +expected_output([], _) -> + []; +expected_output(Bin, _) when is_binary(Bin) -> + Bin. + +%% Convert the given list to a binary with the same encoding as the +%% file name translation mode +s2b(List) -> + Enc = file:native_name_encoding(), + unicode:characters_to_binary(List,Enc,Enc). diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl index 752037042d..5df2bdc13f 100644 --- a/lib/reltool/test/reltool_server_SUITE.erl +++ b/lib/reltool/test/reltool_server_SUITE.erl @@ -60,6 +60,7 @@ end_per_testcase(Func,Config) -> ok; _Fail -> SaveDir = "save."++atom_to_list(Func), + ok = rm_files([SaveDir]), ok = file:make_dir(SaveDir), save_test_result(Files,SaveDir) end, diff --git a/lib/reltool/vsn.mk b/lib/reltool/vsn.mk index 412e78f49f..163b77dfa0 100644 --- a/lib/reltool/vsn.mk +++ b/lib/reltool/vsn.mk @@ -1 +1 @@ -RELTOOL_VSN = 0.6.4 +RELTOOL_VSN = 0.6.5 |