summaryrefslogtreecommitdiff
path: root/lib/sasl
diff options
context:
space:
mode:
authorLukas Larsson <lukas@erlang.org>2021-12-22 11:08:33 +0100
committerGitHub <noreply@github.com>2021-12-22 11:08:33 +0100
commitbfee11fb893266a9476e0e44b637862b2fd1a8dd (patch)
tree335c29188cca06a4cd83af9fdc081bafee14f246 /lib/sasl
parentd2e67fa7faab2761283db0f8e4d9f12f1d47c74c (diff)
parent29a7158d57556183fc209ea4115c39d41c0fa326 (diff)
downloaderlang-bfee11fb893266a9476e0e44b637862b2fd1a8dd.tar.gz
Merge pull request #5427 from kjellwinblad/kjell/erl/make_installation_location_independentR/OTP-17304
Make Erlang installation file system location independent
Diffstat (limited to 'lib/sasl')
-rw-r--r--lib/sasl/doc/src/release_handler.xml20
-rw-r--r--lib/sasl/examples/src/target_system.erl26
-rw-r--r--lib/sasl/src/release_handler.erl149
-rw-r--r--lib/sasl/src/release_handler_1.erl16
-rw-r--r--lib/sasl/test/release_handler_SUITE.erl354
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server-A.rel.src8
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server-B.rel.src8
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server/Emakefile2
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server/ebin/.gitignore1
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server/ebin/hello_server.app7
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server/src/app_callback_module.erl26
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server/src/hello_server.erl20
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server_new/Emakefile2
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server_new/ebin/.gitignore1
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server_new/ebin/hello_server.app7
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server_new/ebin/hello_server.appup3
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server_new/src/app_callback_module.erl26
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server_new/src/hello_server.erl20
18 files changed, 607 insertions, 89 deletions
diff --git a/lib/sasl/doc/src/release_handler.xml b/lib/sasl/doc/src/release_handler.xml
index 55ec4cdb0f..ca6ddd7130 100644
--- a/lib/sasl/doc/src/release_handler.xml
+++ b/lib/sasl/doc/src/release_handler.xml
@@ -202,6 +202,7 @@
</func>
<func>
+ <name since="OTP @OTP-17730@">create_RELEASES(RelDir, RelFile, AppDirs) -> ok | {error, Reason}</name>
<name since="">create_RELEASES(Root, RelDir, RelFile, AppDirs) -> ok | {error, Reason}</name>
<fsummary>Creates an initial <c>RELEASES</c> file.</fsummary>
<type>
@@ -215,12 +216,19 @@
<p>Creates an initial <c>RELEASES</c> file to be used by the
release handler. This file must exist to install new
releases.</p>
- <p><c>Root</c> is the root of the installation (<c>$ROOT</c>) as
- described earlier. <c>RelDir</c> is the directory where
- the <c>RELEASES</c> file is to be created (normally
- <c>$ROOT/releases</c>). <c>RelFile</c> is the name
- of the <c>.rel</c> file that describes the initial release,
- including the extension <c>.rel</c>.</p>
+ <p><c>Root</c> is the root of the installation (<c>$ROOT</c>)
+ as described earlier. <c>RelDir</c> is the directory where the
+ <c>RELEASES</c> file is to be created (normally
+ <c>$ROOT/releases</c>). <c>RelFile</c> is the name of the
+ <c>.rel</c> file that describes the initial release, including
+ the extension <c>.rel</c>. If <c>Root</c> is not given,
+ the <c>RELEASES</c> file will be location independent (i.e, it
+ will not contain absolute paths unless there are absolute
+ paths in <c>AppDirs</c>). A <c>RELEASES</c> file should be
+ made location independent if the installation's <c>$ROOT</c>
+ is unknown. The <c>release_handler</c> module will interpret
+ relative paths in a running system's <c>RELEASES</c> file as
+ being relative to <c>$ROOT</c>.</p>
<p><c>AppDirs</c> can be used to specify from where the modules
for the specified applications are to be loaded. <c>App</c> is
the name of an application, <c>Vsn</c> is the version, and
diff --git a/lib/sasl/examples/src/target_system.erl b/lib/sasl/examples/src/target_system.erl
index b3d6748306..09c9e5254a 100644
--- a/lib/sasl/examples/src/target_system.erl
+++ b/lib/sasl/examples/src/target_system.erl
@@ -35,9 +35,9 @@ create(RelFileName,SystoolsOpts) ->
Dir = filename:dirname(RelFileName),
PlainRelFileName = filename:join(Dir,"plain"),
PlainRelFile = PlainRelFileName ++ ".rel",
- io:fwrite("Reading file: ~tp ...~n", [RelFile]),
+ io:fwrite("Reading file: ~ts ...~n", [RelFile]),
{ok, [RelSpec]} = file:consult(RelFile),
- io:fwrite("Creating file: ~tp from ~tp ...~n",
+ io:fwrite("Creating file: ~ts from ~ts ...~n",
[PlainRelFile, RelFile]),
{release,
{RelName, RelVsn},
@@ -67,32 +67,32 @@ create(RelFileName,SystoolsOpts) ->
make_script(RelFileName,SystoolsOpts),
TarFileName = RelFileName ++ ".tar.gz",
- io:fwrite("Creating tar file ~tp ...~n", [TarFileName]),
+ io:fwrite("Creating tar file ~ts ...~n", [TarFileName]),
make_tar(RelFileName,SystoolsOpts),
TmpDir = filename:join(Dir,"tmp"),
io:fwrite("Creating directory ~tp ...~n",[TmpDir]),
file:make_dir(TmpDir),
- io:fwrite("Extracting ~tp into directory ~tp ...~n", [TarFileName,TmpDir]),
+ io:fwrite("Extracting ~ts into directory ~ts ...~n", [TarFileName,TmpDir]),
extract_tar(TarFileName, TmpDir),
TmpBinDir = filename:join([TmpDir, "bin"]),
ErtsBinDir = filename:join([TmpDir, "erts-" ++ ErtsVsn, "bin"]),
- io:fwrite("Deleting \"erl\" and \"start\" in directory ~tp ...~n",
+ io:fwrite("Deleting \"erl\" and \"start\" in directory ~ts ...~n",
[ErtsBinDir]),
file:delete(filename:join([ErtsBinDir, "erl"])),
file:delete(filename:join([ErtsBinDir, "start"])),
- io:fwrite("Creating temporary directory ~tp ...~n", [TmpBinDir]),
+ io:fwrite("Creating temporary directory ~ts ...~n", [TmpBinDir]),
file:make_dir(TmpBinDir),
- io:fwrite("Copying file \"~ts.boot\" to ~tp ...~n",
+ io:fwrite("Copying file \"~ts.boot\" to ~ts ...~n",
[PlainRelFileName, filename:join([TmpBinDir, "start.boot"])]),
copy_file(PlainRelFileName++".boot",filename:join([TmpBinDir, "start.boot"])),
io:fwrite("Copying files \"epmd\", \"run_erl\" and \"to_erl\" from \n"
- "~tp to ~tp ...~n",
+ "~ts to ~ts ...~n",
[ErtsBinDir, TmpBinDir]),
copy_file(filename:join([ErtsBinDir, "epmd"]),
filename:join([TmpBinDir, "epmd"]), [preserve]),
@@ -104,15 +104,15 @@ create(RelFileName,SystoolsOpts) ->
%% This is needed if 'start' script created from 'start.src' shall
%% be used as it points out this directory as log dir for 'run_erl'
TmpLogDir = filename:join([TmpDir, "log"]),
- io:fwrite("Creating temporary directory ~tp ...~n", [TmpLogDir]),
+ io:fwrite("Creating temporary directory ~ts ...~n", [TmpLogDir]),
ok = file:make_dir(TmpLogDir),
StartErlDataFile = filename:join([TmpDir, "releases", "start_erl.data"]),
- io:fwrite("Creating ~tp ...~n", [StartErlDataFile]),
+ io:fwrite("Creating ~ts ...~n", [StartErlDataFile]),
StartErlData = io_lib:fwrite("~s ~s~n", [ErtsVsn, RelVsn]),
write_file(StartErlDataFile, StartErlData),
- io:fwrite("Recreating tar file ~tp from contents in directory ~tp ...~n",
+ io:fwrite("Recreating tar file ~ts from contents in directory ~ts ...~n",
[TarFileName,TmpDir]),
{ok, Tar} = erl_tar:open(TarFileName, [write, compressed]),
%% {ok, Cwd} = file:get_cwd(),
@@ -125,14 +125,14 @@ create(RelFileName,SystoolsOpts) ->
erl_tar:add(Tar, filename:join(TmpDir,"log"), "log", []),
erl_tar:close(Tar),
%% file:set_cwd(Cwd),
- io:fwrite("Removing directory ~tp ...~n",[TmpDir]),
+ io:fwrite("Removing directory ~ts ...~n",[TmpDir]),
remove_dir_tree(TmpDir),
ok.
install(RelFileName, RootDir) ->
TarFile = RelFileName ++ ".tar.gz",
- io:fwrite("Extracting ~tp ...~n", [TarFile]),
+ io:fwrite("Extracting ~ts ...~n", [TarFile]),
extract_tar(TarFile, RootDir),
StartErlDataFile = filename:join([RootDir, "releases", "start_erl.data"]),
{ok, StartErlData} = read_txt_file(StartErlDataFile),
diff --git a/lib/sasl/src/release_handler.erl b/lib/sasl/src/release_handler.erl
index c3053ef84e..3313f08ff0 100644
--- a/lib/sasl/src/release_handler.erl
+++ b/lib/sasl/src/release_handler.erl
@@ -24,7 +24,7 @@
%% External exports
-export([start_link/0,
- create_RELEASES/1, create_RELEASES/2, create_RELEASES/4,
+ create_RELEASES/1, create_RELEASES/2, create_RELEASES/3, create_RELEASES/4,
unpack_release/1,
check_install_release/1, check_install_release/2,
install_release/1, install_release/2, new_emulator_upgrade/2,
@@ -43,7 +43,13 @@
-export([do_write_release/3, do_copy_file/2, do_copy_files/2,
do_copy_files/1, do_rename_files/1, do_remove_files/1,
remove_file/1, do_write_file/2, do_write_file/3,
- do_ensure_RELEASES/1]).
+ do_ensure_RELEASES/1,
+ consult/2,
+ root_dir_relative_read_file_info/1,
+ root_dir_relative_read_file/1,
+ root_dir_relative_rename_file/2,
+ root_dir_relative_make_dir/1,
+ root_dir_relative_ensure_dir/1]).
-record(state, {unpurged = [],
root,
@@ -378,6 +384,8 @@ create_RELEASES([Root, RelFile | LibDirs]) ->
create_RELEASES(Root, RelFile) ->
create_RELEASES(Root, filename:join(Root, "releases"), RelFile, []).
+create_RELEASES(RelDir, RelFile, LibDirs) ->
+ create_RELEASES("", RelDir, RelFile, LibDirs).
create_RELEASES(Root, RelDir, RelFile, LibDirs) ->
case catch check_rel(Root, RelFile, LibDirs, false) of
{error, Reason } ->
@@ -397,7 +405,8 @@ create_RELEASES(Root, RelDir, RelFile, LibDirs) ->
%% located under Dir/ebin
%% Purpose: Upgrade to the version in Dir according to an appup file
%%-----------------------------------------------------------------
-upgrade_app(App, NewDir) ->
+upgrade_app(App, NewDir1) ->
+ NewDir = root_dir_relative_path(NewDir1),
try upgrade_script(App, NewDir) of
{ok, NewVsn, Script} ->
eval_appup_script(App, NewVsn, NewDir, Script)
@@ -435,7 +444,8 @@ downgrade_app(App, OldVsn, OldDir) ->
{error, Reason}
end.
-upgrade_script(App, NewDir) ->
+upgrade_script(App, NewDir1) ->
+ NewDir = root_dir_relative_path(NewDir1),
OldVsn = ensure_running(App),
OldDir = code:lib_dir(App),
{NewVsn, Script} = find_script(App, NewDir, OldVsn, up),
@@ -489,7 +499,8 @@ ensure_running(App) ->
end.
find_script(App, Dir, OldVsn, UpOrDown) ->
- Appup = filename:join([Dir, "ebin", atom_to_list(App)++".appup"]),
+ Appup1 = filename:join([Dir, "ebin", atom_to_list(App)++".appup"]),
+ Appup = root_dir_relative_path(Appup1),
case file:consult(Appup) of
{ok, [{NewVsn, UpFromScripts, DownToScripts}]} ->
Scripts = case UpOrDown of
@@ -522,7 +533,7 @@ read_app(App, Vsn, Dir) ->
read_appspec(App, Dir) ->
AppS = atom_to_list(App),
- Path = [filename:join(Dir, "ebin")],
+ Path = [root_dir_relative_path(filename:join(Dir, "ebin"))],
case file:path_consult(Path, AppS++".app") of
{ok, AppSpecL, _File} ->
AppSpecL;
@@ -833,7 +844,9 @@ do_unpack_release(Root, RelDir, ReleaseName, Releases) ->
Rel = ReleaseName ++ ".rel",
_ = extract_rel_file(filename:join("releases", Rel), Tar, Root),
RelFile = filename:join(RelDir, Rel),
- Release = check_rel(Root, RelFile, false),
+ %% Send an empty string as Root as the library locations should
+ %% appear as paths relative to the root
+ Release = check_rel("", RelFile, false),
#release{vsn = Vsn} = Release,
case lists:keysearch(Vsn, #release.vsn, Releases) of
{value, _} -> throw({error, {existing_release, Vsn}});
@@ -850,8 +863,8 @@ do_unpack_release(Root, RelDir, ReleaseName, Releases) ->
copy_file(RelFile, Dir, false),
%% Clean release
- _ = file:delete(Tar),
- _ = file:delete(RelFile),
+ _ = root_dir_relative_file_delete(Tar),
+ _ = root_dir_relative_file_delete(RelFile),
{ok, NewReleases, Vsn}.
@@ -883,7 +896,19 @@ check_rel_data({release, {Name, Vsn}, {erts, EVsn}, Libs}, Root, LibDirs,
check_path(Path, Masters),
Path;
_ ->
- filename:join([Root, "lib", LibName])
+ %% If Root is an empty string,
+ %% we assume that the path is
+ %% relative to code:root_dir()
+ %% and save a relative
+ %% path. This is done to make it
+ %% easy to create a relocatable
+ %% RELEASES file.
+ case string:length(Root) of
+ 0 ->
+ filename:join("lib", LibName);
+ _ ->
+ filename:join([Root, "lib", LibName])
+ end
end,
{Lib, LibVsn, LibDir}
end,
@@ -894,7 +919,7 @@ check_rel_data(RelData, _Root, _LibDirs, _Masters) ->
throw({error, {bad_rel_data, RelData}}).
check_path(Path) ->
- check_path_response(Path, file:read_file_info(Path)).
+ check_path_response(Path, root_dir_relative_read_file_info(Path)).
check_path(Path, false) -> check_path(Path);
check_path(Path, Masters) -> check_path_master(Masters, Path).
@@ -904,7 +929,7 @@ check_path(Path, Masters) -> check_path_master(Masters, Path).
%% at one node it should not exist at any other node either.
%%-----------------------------------------------------------------
check_path_master([Master|Ms], Path) ->
- case rpc:call(Master, file, read_file_info, [Path]) of
+ case rpc:call(Master, ?MODULE, root_dir_relative_read_file_info, [Path]) of
{badrpc, _} -> consult_master(Ms, Path);
Res -> check_path_response(Path, Res)
end;
@@ -1573,14 +1598,14 @@ memlib(_Lib, []) -> false.
%% recursively remove file or directory
remove_file(File) ->
- case file:read_link_info(File) of
+ case root_dir_relative_read_link_info(File) of
{ok, Info} when Info#file_info.type==directory ->
- case file:list_dir(File) of
+ case root_dir_relative_list_dir(File) of
{ok, Files} ->
lists:foreach(fun(File2) ->
remove_file(filename:join(File,File2))
end, Files),
- case file:del_dir(File) of
+ case root_dir_relative_dir_delete(File) of
ok -> ok;
{error, Reason} -> throw({error, Reason})
end;
@@ -1588,7 +1613,7 @@ remove_file(File) ->
throw({error, Reason})
end;
{ok, _Info} ->
- case file:delete(File) of
+ case root_dir_relative_file_delete(File) of
ok -> ok;
{error, Reason} -> throw({error, Reason})
end;
@@ -1599,7 +1624,8 @@ remove_file(File) ->
do_write_file(File, Str) ->
do_write_file(File, Str, []).
-do_write_file(File, Str, FileOpts) ->
+do_write_file(File1, Str, FileOpts) ->
+ File = root_dir_relative_path(File1),
case file:open(File, [write | FileOpts]) of
{ok, Fd} ->
io:put_chars(Fd, Str),
@@ -1822,13 +1848,13 @@ check_file_masters(_FileName, _Type, []) ->
%% Type == regular | directory
do_check_file(FileName, Type) ->
- case file:read_file_info(FileName) of
+ case root_dir_relative_read_file_info(FileName) of
{ok, Info} when Info#file_info.type==Type -> ok;
{error, _Reason} -> throw({error, {no_such_file, FileName}})
end.
do_check_file(Master, FileName, Type) ->
- case rpc:call(Master, file, read_file_info, [FileName]) of
+ case rpc:call(Master, ?MODULE, root_dir_relative_read_file_info, [FileName]) of
{ok, Info} when Info#file_info.type==Type -> ok;
_ -> throw({error, {no_such_file, {Master, FileName}}})
end.
@@ -1871,7 +1897,8 @@ write_releases_1(Dir, NewReleases, Masters) ->
write_releases_m(Dir, NewReleases, Masters).
do_write_release(Dir, RELEASES, NewReleases) ->
- case file:open(filename:join(Dir, RELEASES), [write,{encoding,utf8}]) of
+ ReleasesFile = root_dir_relative_path(filename:join(Dir, RELEASES)),
+ case file:open(ReleasesFile, [write,{encoding,utf8}]) of
{ok, Fd} ->
ok = io:format(Fd, "%% ~s~n~tp.~n",
[epp:encoding_to_string(utf8),NewReleases]),
@@ -1908,7 +1935,8 @@ write_releases_m(Dir, NewReleases, Masters) ->
remove_files(all, [Backup, Change], Masters),
ok;
{error, {Master, R}} ->
- takewhile(Master, Masters, file, rename,
+ takewhile(Master, Masters, ?MODULE,
+ root_dir_relative_rename_file,
[Backup, RelFile]),
remove_files(all, [Backup, Change], Masters),
throw({error, {Master, R, move_releases}})
@@ -1959,9 +1987,9 @@ do_copy_file(File, Dir) ->
do_copy_file1(File, File2).
do_copy_file1(File, File2) ->
- case file:read_file(File) of
+ case root_dir_relative_read_file(File) of
{ok, Bin} ->
- case file:write_file(File2, Bin) of
+ case root_dir_relative_write_file(File2, Bin) of
ok -> ok;
{error, Reason} ->
{error, {Reason, File2}}
@@ -1995,7 +2023,9 @@ do_copy_files([]) ->
%%-----------------------------------------------------------------
%% Rename each Src file to Dest file in the list of files.
%%-----------------------------------------------------------------
-do_rename_files([{Src, Dest}|Files]) ->
+do_rename_files([{Src1, Dest1}|Files]) ->
+ Src = root_dir_relative_path(Src1),
+ Dest = root_dir_relative_path(Dest1),
case file:rename(Src, Dest) of
ok -> do_rename_files(Files);
Error -> Error
@@ -2007,7 +2037,7 @@ do_rename_files([]) ->
%% Remove a list of files. Ignore failure.
%%-----------------------------------------------------------------
do_remove_files([File|Files]) ->
- _ = file:delete(File),
+ _ = root_dir_relative_file_delete(File),
do_remove_files(Files);
do_remove_files([]) ->
ok.
@@ -2018,7 +2048,7 @@ do_remove_files([]) ->
%% If not create an empty RELEASES file.
%%-----------------------------------------------------------------
do_ensure_RELEASES(RelFile) ->
- case file:read_file_info(RelFile) of
+ case root_dir_relative_read_file_info(RelFile) of
{ok, _} -> ok;
_ -> do_write_file(RelFile, "[]. ")
end.
@@ -2026,11 +2056,16 @@ do_ensure_RELEASES(RelFile) ->
%%-----------------------------------------------------------------
%% Make a directory, ignore failures (captured later).
%%-----------------------------------------------------------------
-make_dir(Dir, false) ->
+make_dir(Dir1, false) ->
+ Dir = root_dir_relative_path(Dir1),
_ = file:make_dir(Dir),
ok;
make_dir(Dir, Masters) ->
- lists:foreach(fun(Master) -> rpc:call(Master, file, make_dir, [Dir]) end,
+ lists:foreach(fun(Master) -> rpc:call(Master,
+ ?MODULE,
+ root_dir_relative_make_dir,
+ [Dir])
+ end,
Masters).
%%-----------------------------------------------------------------
@@ -2068,7 +2103,7 @@ takewhile(Master, Masters, M, F, A) ->
end, Masters),
ok.
-consult(File, false) -> file:consult(File);
+consult(File, false) -> file:consult(root_dir_relative_path(File));
consult(File, Masters) -> consult_master(Masters, File).
%%-----------------------------------------------------------------
@@ -2077,7 +2112,7 @@ consult(File, Masters) -> consult_master(Masters, File).
%% not exist at any other node either.
%%-----------------------------------------------------------------
consult_master([Master|Ms], File) ->
- case rpc:call(Master, file, consult, [File]) of
+ case rpc:call(Master, ?MODULE, consult, [File, false]) of
{badrpc, _} -> consult_master(Ms, File);
Res -> Res
end;
@@ -2085,12 +2120,12 @@ consult_master([], _File) ->
{error, no_master}.
read_file(File, false) ->
- file:read_file(File);
+ root_dir_relative_read_file(File);
read_file(File, Masters) ->
read_master(Masters, File).
write_file(File, Data, false) ->
- case file:write_file(File, Data) of
+ case root_dir_relative_write_file(File, Data) of
ok -> ok;
Error -> throw(Error)
end;
@@ -2101,12 +2136,12 @@ write_file(File, Data, Masters) ->
end.
ensure_dir(File, false) ->
- case filelib:ensure_dir(File) of
+ case root_dir_relative_ensure_dir(File) of
ok -> ok;
Error -> throw(Error)
end;
ensure_dir(File, Masters) ->
- case at_all_masters(Masters,filelib,ensure_dir,[File]) of
+ case at_all_masters(Masters,?MODULE,root_dir_relative_ensure_dir,[File]) of
ok -> ok;
Error -> throw(Error)
end.
@@ -2130,7 +2165,7 @@ remove_files(Master, Files, Masters) ->
%% not exist at any other node either.
%%-----------------------------------------------------------------
read_master([Master|Ms], File) ->
- case rpc:call(Master, file, read_file, [File]) of
+ case rpc:call(Master, ?MODULE, root_dir_relative_read_file, [File]) of
{badrpc, _} -> read_master(Ms, File);
Res -> Res
end;
@@ -2299,3 +2334,47 @@ get_releases_with_status([ {_, _, _, ReleaseStatus } = Head | Tail],
get_releases_with_status(Tail, Status, [Head | Acc]);
get_releases_with_status([_ | Tail], Status, Acc) ->
get_releases_with_status(Tail, Status, Acc).
+
+root_dir_relative_read_link_info(File) ->
+ file:read_link_info(root_dir_relative_path(File)).
+
+root_dir_relative_list_dir(File) ->
+ file:list_dir(root_dir_relative_path(File)).
+
+root_dir_relative_file_delete(File) ->
+ file:delete(root_dir_relative_path(File)).
+
+root_dir_relative_dir_delete(File) ->
+ file:del_dir(root_dir_relative_path(File)).
+
+root_dir_relative_path(Pathname) ->
+ case filename:pathtype(Pathname) of
+ relative ->
+ filename:join(code:root_dir(), Pathname);
+ _ ->
+ Pathname
+ end.
+
+root_dir_relative_write_file(File, Bin) ->
+ file:write_file(root_dir_relative_path(File), Bin).
+
+%% The root_dir_relative* functions below are exported so that they
+%% can be called on other nodes with rpc
+
+root_dir_relative_read_file_info(Path) ->
+ file:read_file_info(root_dir_relative_path(Path)).
+
+root_dir_relative_read_file(File) ->
+ file:read_file(root_dir_relative_path(File)).
+
+root_dir_relative_rename_file(Source1, Destination1) ->
+ Source = root_dir_relative_path(Source1),
+ Destination = root_dir_relative_path(Destination1),
+ file:rename(Source, Destination).
+
+root_dir_relative_make_dir(Dir) ->
+ file:make_dir(root_dir_relative_path(Dir)).
+
+root_dir_relative_ensure_dir(Dir) ->
+ filelib:ensure_dir(root_dir_relative_path(Dir)).
+
diff --git a/lib/sasl/src/release_handler_1.erl b/lib/sasl/src/release_handler_1.erl
index ede7df186b..cc46fa5133 100644
--- a/lib/sasl/src/release_handler_1.erl
+++ b/lib/sasl/src/release_handler_1.erl
@@ -313,7 +313,7 @@ eval({load_object_code, {Lib, LibVsn, Modules}}, EvalState) ->
{NewBins, NewVsns} =
lists:foldl(fun(Mod, {Bins, Vsns}) ->
File = lists:concat([Mod, Ext]),
- FName = filename:join([LibDir, "ebin", File]),
+ FName = root_dir_relative_path(filename:join([LibDir, "ebin", File])),
case erl_prim_loader:get_file(FName) of
{ok, Bin, FName2} ->
NVsns = add_vsns(Mod, Bin, Vsns),
@@ -340,14 +340,15 @@ eval(point_of_no_return, EvalState) ->
EvalState#eval_state.libdirs
end,
lists:foreach(fun({Lib, _LibVsn, LibDir}) ->
- Ebin = filename:join(LibDir,"ebin"),
+ Ebin = root_dir_relative_path(filename:join(LibDir,"ebin")),
code:replace_path(Lib, Ebin)
end,
Libs),
EvalState;
eval({load, {Mod, _PrePurgeMethod, PostPurgeMethod}}, EvalState) ->
Bins = EvalState#eval_state.bins,
- {value, {_Mod, Bin, File}} = lists:keysearch(Mod, 1, Bins),
+ {value, {_Mod, Bin, File1}} = lists:keysearch(Mod, 1, Bins),
+ File = root_dir_relative_path(File1),
% load_binary kills all procs running old code
% if soft_purge, we know that there are no such procs now
{module,_} = code:load_binary(Mod, File, Bin),
@@ -804,3 +805,12 @@ get_vsn(Bin) ->
Vsn
end
end.
+
+root_dir_relative_path(Pathname) ->
+ case filename:pathtype(Pathname) of
+ relative ->
+ filename:join(code:root_dir(), Pathname);
+ _ ->
+ Pathname
+ end.
+
diff --git a/lib/sasl/test/release_handler_SUITE.erl b/lib/sasl/test/release_handler_SUITE.erl
index bb23b03130..78626bbc91 100644
--- a/lib/sasl/test/release_handler_SUITE.erl
+++ b/lib/sasl/test/release_handler_SUITE.erl
@@ -20,6 +20,8 @@
-module(release_handler_SUITE).
-include_lib("common_test/include/ct.hrl").
+-include_lib("stdlib/include/assert.hrl").
+-include_lib("kernel/include/file.hrl").
-compile([export_all, nowarn_export_all]).
-export([scheduler_wall_time/0, garbage_collect/0]). %% rpc'ed
@@ -59,17 +61,16 @@ win32_cases() ->
%% Cases that can be run on all platforms
cases() ->
- [otp_2740, otp_2760, otp_5761, otp_9402, otp_9417,
- otp_9395_check_old_code, otp_9395_check_and_purge,
- otp_9395_update_many_mods, otp_9395_rm_many_mods,
+ [otp_9395_check_old_code,
instructions, eval_appup, eval_appup_with_restart,
- supervisor_which_children_timeout,
- release_handler_which_releases, install_release_syntax_check,
- upgrade_supervisor, upgrade_supervisor_fail, otp_9864,
- otp_10463_upgrade_script_regexp, no_dot_erlang, unicode_upgrade].
+ install_release_syntax_check,
+ otp_10463_upgrade_script_regexp, no_dot_erlang, move_system,
+ {group, absolute}, {group, relative}].
groups() ->
- [{release,[],
+ [{absolute,[],root_dir_cases()},
+ {relative,[],root_dir_cases()},
+ {release,[],
[
{group,release_single},
{group,release_gg}
@@ -86,6 +87,22 @@ groups() ->
upgrade_gg
]}].
+%% Testcases that are to be run with and without an absolute root dir
+root_dir_cases() ->
+ [supervisor_which_children_timeout,
+ release_handler_which_releases,
+ otp_2760,
+ otp_5761,
+ otp_9402,
+ otp_9417,
+ otp_9395_check_and_purge,
+ otp_9395_update_many_mods,
+ otp_9395_rm_many_mods,
+ otp_9864,
+ upgrade_supervisor,
+ upgrade_supervisor_fail,
+ unicode_upgrade].
+
%% {group,release}
%% Top group for all cases using run_erl
init_per_group(release, Config) ->
@@ -160,8 +177,10 @@ init_per_group(release_gg, Config0) ->
Snames),
test_server:timetrap_cancel(Dog),
- [{snames,Snames}|Config].
-
+ [{snames,Snames}|Config];
+init_per_group(Group, Config) when Group =:= absolute;
+ Group =:= relative ->
+ [{root_dir, Group}|Config].
end_per_group(release, Config) ->
Dog = test_server:timetrap(?default_timeout),
@@ -243,6 +262,252 @@ gg_node_snames(Config) ->
%%%-----------------------------------------------------------------
%%% TEST CASES
+%% Test that a release can be location independent (i.e., if all
+%% paths related to the release are relative to the ROOTDIR of the
+%% release, then one can move the release to a different directory and
+%% it should still work).
+move_system(Conf) when is_list(Conf) ->
+
+ DataDir = ?config(data_dir, Conf),
+ TestRootDir = filename:join(priv_dir(Conf), ?FUNCTION_NAME),
+ ErtsBinDir = filename:join("erts-" ++ find_vsn_app(erts), "bin"),
+
+ %% Remove old test data
+ file:del_dir_r(filename:join(TestRootDir,"system")),
+ file:del_dir_r(filename:join(TestRootDir,"system_moved")),
+ file:del_dir_r(filename:join(TestRootDir,"system_moved_again")),
+
+ %% Create TAR file for release A
+ ReleaseATarFile = create_release_package(DataDir, "hello_server", "A"),
+ SystemPath = filename:join(TestRootDir, "system"),
+ ok = erl_tar:extract(ReleaseATarFile, [{cwd, SystemPath}, compressed]),
+ RelDir = filename:join(SystemPath, "releases"),
+ SystemRelFile = filename:join(RelDir, "hello_server-A.rel"),
+
+ %% Create a location independent RELEASES file. Library paths in the releases
+ %% file are assumed to be relative to the RootDir.
+ ok = release_handler:create_RELEASES(RelDir, SystemRelFile, []),
+
+ %% Create the bin directory with links (or copies on windows) to the erts directory
+ file:make_dir(filename:join(SystemPath,"bin")),
+ case os:type() of
+ {win32, _} ->
+ {ok,_} = file:copy(
+ filelib:wildcard(filename:join([SystemPath,ErtsBinDir,"erl.exe"])),
+ filename:join([SystemPath,"bin","erl.exe"]));
+ _ ->
+ ok = file:make_symlink(
+ filename:join(["..",ErtsBinDir,"erl"]),
+ filename:join([SystemPath,"bin","erl"]))
+ end,
+
+ %% Test that the location independent system can start and run
+ {ok, Peer, Node} = start_remote_node(SystemPath, "A"),
+ hello = erpc:call(Node, app_callback_module, get_response, []),
+ peer:stop(Peer),
+
+ %% Should still work after copying the system and corrupting the source
+ NewSystemPath = filename:join(TestRootDir, "system_moved"),
+ copy_r(SystemPath, NewSystemPath),
+ [file:del_dir_r(F) || F <- filelib:wildcard(filename:join([SystemPath,"lib","stdlib*"]))],
+ file:del_dir_r(filename:join(SystemPath,"releases")),
+ {ok, MovedPeer, MovedNode} = start_remote_node(NewSystemPath, "A"),
+ hello = erpc:call(MovedNode, app_callback_module, get_response, []),
+ peer:stop(MovedPeer),
+
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ %% Let us now try if a system upgrade also works with a location independent system %
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ ReleaseBTarFile = create_release_package(DataDir, "hello_server_new", "B"),
+
+ BPackDest = filename:join([NewSystemPath, "releases", filename:basename(ReleaseBTarFile)]),
+ {ok, _ } = file:copy(ReleaseBTarFile, BPackDest),
+ BTarFileName = filename:basename(ReleaseBTarFile),
+ NameToUnpack = filename:rootname(BTarFileName, ".tar.gz"),
+ %% Start remote node with release A
+ {ok, PeerA, NodeA} = start_remote_node(NewSystemPath, "A"),
+
+ %% Set current working directory to something irrelevant as the
+ %% current working directory should not affect if a system is
+ %% location independent or not
+ ok = erpc:call(NodeA, file, set_cwd, ["/"]),
+ %% Let us check our app
+ hello = erpc:call(NodeA, app_callback_module, get_response, []),
+ %% Install the next release
+ {ok, "B"} = erpc:call(NodeA, release_handler, unpack_release, [NameToUnpack]),
+ %% We can now create relup file
+ AppUpSrc = filename:join([DataDir, "relocatable_release", "hello_server_new",
+ "ebin", "hello_server.appup"]),
+ AppUpDest = filename:join([NewSystemPath, "lib", "hello_server-B", "ebin", "hello_server.appup"]),
+ {ok, _} = file:copy(AppUpSrc, AppUpDest),
+ %% Run command to create the relup file
+ ok = systools:make_relup(
+ filename:join([NewSystemPath, "releases", "B", "hello_server-B"]),
+ [filename:join([NewSystemPath, "releases", "A", "hello_server-A"])],
+ [filename:join([NewSystemPath, "releases", "A", "hello_server-A"])],
+ [{outdir,filename:join([NewSystemPath, "releases","B"])},
+ {path,[NewSystemPath ++ "/lib/hello_server-A/ebin/",
+ NewSystemPath ++ "/lib/hello_server-B/ebin/"]}]
+ ),
+ %% Install the B version
+ {ok, "A", _} = erpc:call(NodeA, release_handler, install_release, ["B"]),
+ %% Check that the releases info looks ok
+ true = lists:any(fun({"hello_server", "B", _, current}) ->
+ true;
+ (_) ->
+ false
+ end,
+ erpc:call(NodeA, release_handler, which_releases, [])),
+ %% Make sure the old module gets replaced
+ ok = erpc:call(NodeA, app_callback_module, update, []),
+ %% Check that the upgrade worked
+ hej = erpc:call(NodeA, app_callback_module, get_response, []),
+
+ case os:type() of
+ {win32, _} ->
+ %% We cannot make release permanent on windows due to
+ %% not having permissions to edit services.
+ %% And symlinks to do not on windows, so we don't test
+ %% anything more there.
+ peer:stop(PeerA);
+ _ ->
+ move_system_unix(NodeA, PeerA, TestRootDir, ErtsBinDir, NewSystemPath)
+
+ end.
+
+move_system_unix(NodeA, PeerA, TestRootDir, ErtsBinDir, NewSystemPath) ->
+ %% Make the upgrade permanent
+ ok = erpc:call(NodeA, release_handler, make_permanent, ["B"]),
+ %% Check that it still works
+ hej = erpc:call(NodeA, app_callback_module, get_response, []),
+ true = lists:any(fun({"hello_server", "B", _, permanent}) ->
+ true;
+ (_) ->
+ false
+ end,
+ erpc:call(NodeA, release_handler, which_releases, [])),
+ ok = peer:stop(PeerA),
+
+ %% We will now move the install and check that everything still seems to be working fine
+ NewSystemPath2 = filename:join(TestRootDir, "system_moved_again"),
+ ok = file:rename(NewSystemPath, NewSystemPath2),
+
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ %% Create a symlink to moved system and test that if we set the path to
+ %% contain the symlink it will work.
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ ok = file:make_symlink(
+ filename:join([NewSystemPath2, ErtsBinDir, "erl"]),
+ filename:join(TestRootDir, "erl")),
+
+ Name = peer:random_name(),
+ LinkNode = list_to_atom(Name++"@"++lists:last(string:split(atom_to_list(node()),"@"))),
+
+ %% We cannot use ?CT_PEER here because it uses spawn_executable and that
+ %% does not search the PATH for which program to run.
+ Port = open_port(
+ {spawn,"erl -sname " ++ Name ++ " -boot " ++
+ filename:join([NewSystemPath2, "releases", "B", "start"])},
+ [{env,[{"PATH",TestRootDir ++ ":" ++ os:getenv("PATH")}]}]),
+
+ %% Wait for node to start
+ receive _ -> ok end,
+
+ hej = erpc:call(LinkNode, app_callback_module, get_response, []),
+
+ true = catch port_close(Port),
+ ct:log("~p",[(fun F() -> receive M -> [M | F()] after 0 -> [] end end)()]),
+
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ %% Test that we can do a downgrade of the moved system
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ {ok, PeerB, NodeB} = start_remote_node(NewSystemPath2, "B"),
+
+ %% Change current working directory to something irrelevant
+ ok = erpc:call(NodeB, file, set_cwd, ["/"]),
+ {ok, "/"} = erpc:call(NodeB, file, get_cwd, []),
+ %% Check that we are using version B
+ hej = erpc:call(NodeB, app_callback_module, get_response, []),
+ %% Downgrade to version A
+ {ok, "A", _} = erpc:call(NodeB, release_handler, install_release, ["A"]),
+ true = lists:any(fun({"hello_server", "A", _, current}) ->
+ true;
+ (_) ->
+ false
+ end,
+ erpc:call(NodeB, release_handler, which_releases, [])),
+ %% Make sure that the module is reloaded
+ ok = erpc:call(NodeB, app_callback_module, update, []),
+ %% Make sure that we are on version A
+ hello = erpc:call(NodeB, app_callback_module, get_response, []),
+ %% Make the downgrade permanent
+ ok = erpc:call(NodeB, release_handler, make_permanent, ["A"]),
+ %% Test that remove release works
+ ok = erpc:call(NodeB, release_handler, remove_release, ["B"]),
+ %% B should not exist anymore
+ false = lists:any(fun({"hello_server", "B", _, _}) ->
+ true;
+ (_) ->
+ false
+ end,
+ erpc:call(NodeB, release_handler, which_releases, [])),
+ ok = erpc:call(NodeB, app_callback_module, update, []),
+ %% We should still get hello
+ hello = erpc:call(NodeB, app_callback_module, get_response, []),
+ ok = peer:stop(PeerB),
+
+ ok.
+
+start_remote_node(SystemPath, RelVsn) ->
+ SystemErlPath = filename:join([SystemPath, "bin", "erl"]),
+ ?CT_PEER(#{ exec => SystemErlPath,
+ args => ["-boot",filename:join([SystemPath,"releases", RelVsn, "start"])]}).
+
+create_release_package(DataDir, SourceDir, RelVsn) ->
+ ReleaseSource = filename:join(DataDir, "relocatable_release"),
+ AppSrc = filename:join(ReleaseSource, SourceDir),
+ OldCWD = file:get_cwd(),
+ file:set_cwd(AppSrc),
+ os:cmd("erl -make"),
+ file:set_cwd(OldCWD),
+ RelFileScr = filename:join(ReleaseSource, "hello_server-"++ RelVsn ++".rel.src"),
+ RelFileDst = filename:join(ReleaseSource, "hello_server-"++ RelVsn ++".rel"),
+ {ok, RelFileTxt1} = file:read_file(RelFileScr),
+ RelFileTxt = fix_rel_file_vsns(["erts", "kernel", "stdlib", "sasl"],
+ RelFileTxt1),
+ ok = file:write_file(RelFileDst, RelFileTxt),
+ AppPath = filename:join([ReleaseSource, SourceDir, "ebin"]),
+ RelFileWithoutEnding = filename:join(ReleaseSource, "hello_server-" ++ RelVsn),
+ ok = systools:make_script(RelFileWithoutEnding, [local, {path, [AppPath]}]),
+ ok = systools:make_tar(RelFileWithoutEnding, [{erts, code:root_dir()}, {path, [AppPath]}]),
+ SystemPath = filename:join(ReleaseSource, "system"),
+ file:make_dir(SystemPath),
+ InitialTarPath = filename:join(ReleaseSource, "hello_server-"++ RelVsn ++".tar.gz"),
+ InitialTarPath.
+
+fix_rel_file_vsns(Apps, Txt) ->
+ Res =
+ lists:foldl(
+ fun(App, TxtAcc) ->
+ string:replace(TxtAcc,
+ "%" ++ App ++ "_VSN" ++ "%" ,
+ find_vsn_app(erlang:list_to_atom(App)))
+ end,
+ Txt,
+ Apps),
+ erlang:iolist_to_binary(lists:flatten(Res)).
+
+find_vsn_app(erts) ->
+ Str = erlang:system_info(system_version),
+ {match, [{Start, Len} | _]} = re:run(Str, "erts-(\\d\\.?)+"),
+ ErtsStr = string:substr(Str, Start+1, Len),
+ [_, Vsn] = string:split(ErtsStr, "-"),
+ Vsn;
+find_vsn_app(App) ->
+ Apps = application:which_applications(),
+ [Vsn] = [Vsn || {AppX, _, Vsn} <- Apps, AppX =:= App],
+ Vsn.
+
%% Executed instead of release group when no run_erl program exists
no_run_erl(Config) when is_list(Config) ->
@@ -617,7 +882,7 @@ supervisor_which_children_timeout(Conf) ->
DataDir = ?config(data_dir,Conf),
LibDir = filename:join([DataDir,release_handler_timeouts]),
- Rel1 = create_and_install_fake_first_release(Dir,[{dummy,"0.1",LibDir}]),
+ Rel1 = create_and_install_fake_first_release(Conf,Dir,[{dummy,"0.1",LibDir}]),
{ok, Node} = t_start_node(supervisor_which_children_timeout, Rel1, []),
Proc = rpc:call(Node, erlang, whereis, [dummy_sup_2]),
@@ -656,7 +921,7 @@ release_handler_which_releases(Conf) ->
DataDir = ?config(data_dir,Conf),
LibDir = filename:join([DataDir,release_handler_timeouts]),
- Rel1 = create_and_install_fake_first_release(Dir,[{dummy,"0.1",LibDir}]),
+ Rel1 = create_and_install_fake_first_release(Conf,Dir,[{dummy,"0.1",LibDir}]),
{ok, Node} = t_start_node(release_handler_which_releases, Rel1, []),
Releases0 = rpc:call(Node, release_handler, which_releases, []),
@@ -712,7 +977,7 @@ otp_2760(Conf) ->
DataDir = ?config(data_dir,Conf),
LibDir = filename:join([DataDir,app1_app2,lib1]),
- Rel1 = create_and_install_fake_first_release(Dir,[{app1,"1.0",LibDir}]),
+ Rel1 = create_and_install_fake_first_release(Conf,Dir,[{app1,"1.0",LibDir}]),
Rel2 = create_fake_upgrade_release(Dir,"after",[],{[Rel1],[Rel1],[LibDir]}),
Rel2Dir = filename:dirname(Rel2),
@@ -750,7 +1015,7 @@ otp_5761(Conf) when is_list(Conf) ->
LibDir2 = filename:join(RelDir, "lib2"),
%% Create the releases
- Rel1 = create_and_install_fake_first_release(Dir,
+ Rel1 = create_and_install_fake_first_release(Conf,Dir,
[{app1,"1.0",LibDir1},
{app2,"1.0",LibDir1}]),
Rel2 = create_fake_upgrade_release(Dir,
@@ -829,7 +1094,7 @@ otp_9402(Conf) when is_list(Conf) ->
LibDir = filename:join(?config(data_dir, Conf), "lib"),
%% Create the releases
- Rel1 = create_and_install_fake_first_release(Dir,
+ Rel1 = create_and_install_fake_first_release(Conf,Dir,
[{a,"1.1",LibDir}]),
Rel2 = create_fake_upgrade_release(Dir,
"2",
@@ -896,7 +1161,7 @@ otp_9417(Conf) when is_list(Conf) ->
LibDir = filename:join(?config(data_dir, Conf), "lib"),
%% Create the releases
- Rel1 = create_and_install_fake_first_release(Dir,
+ Rel1 = create_and_install_fake_first_release(Conf,Dir,
[{b,"1.0",LibDir}]),
Rel2 = create_fake_upgrade_release(Dir,
"2",
@@ -1009,7 +1274,7 @@ otp_9395_check_and_purge(Conf) when is_list(Conf) ->
LibDir = filename:join(?config(data_dir, Conf), "lib"),
%% Create the releases
- Rel1 = create_and_install_fake_first_release(Dir,
+ Rel1 = create_and_install_fake_first_release(Conf,Dir,
[{b,"1.0",LibDir}]),
Rel2 = create_fake_upgrade_release(Dir,
"2",
@@ -1075,7 +1340,7 @@ otp_9395_update_many_mods(Conf) when is_list(Conf) ->
LibDir = filename:join(?config(data_dir, Conf), "lib"),
%% Create the releases
- Rel1 = create_and_install_fake_first_release(Dir,
+ Rel1 = create_and_install_fake_first_release(Conf,Dir,
[{many_mods,"1.0",LibDir}]),
Rel2 = create_fake_upgrade_release(Dir,
"2",
@@ -1190,7 +1455,7 @@ otp_9395_rm_many_mods(Conf) when is_list(Conf) ->
LibDir = filename:join(?config(data_dir, Conf), "lib"),
%% Create the releases
- Rel1 = create_and_install_fake_first_release(Dir,
+ Rel1 = create_and_install_fake_first_release(Conf,Dir,
[{many_mods,"1.0",LibDir}]),
Rel2 = create_fake_upgrade_release(Dir,
"2",
@@ -1302,7 +1567,7 @@ do_otp_9864(Conf) ->
LibDir2 = filename:join(Dir, "lib2"),
%% Create the releases
- Rel1 = create_and_install_fake_first_release(Dir,
+ Rel1 = create_and_install_fake_first_release(Conf,Dir,
[{app1,"1.0",LibDir1},
{app2,"1.0",LibDir1}]),
Rel2 = create_fake_upgrade_release(Dir,
@@ -1358,7 +1623,7 @@ upgrade_supervisor(Conf) when is_list(Conf) ->
%% Create the releases
Lib1 = [{a,"1.0",LibDir}],
Lib2 = [{a,"9.0",LibDir}],
- Rel1 = create_and_install_fake_first_release(Dir,Lib1),
+ Rel1 = create_and_install_fake_first_release(Conf,Dir,Lib1),
Rel2 = create_fake_upgrade_release(Dir,"2",Lib2,{[Rel1],[Rel1],[LibDir]}),
Rel1Dir = filename:dirname(Rel1),
Rel2Dir = filename:dirname(Rel2),
@@ -1415,7 +1680,7 @@ upgrade_supervisor_fail(Conf) when is_list(Conf) ->
%% Create the releases
Lib1 = [{a,"1.0",LibDir}],
Lib2 = [{a,"9.1",LibDir}],
- Rel1 = create_and_install_fake_first_release(Dir,Lib1),
+ Rel1 = create_and_install_fake_first_release(Conf,Dir,Lib1),
Rel2 = create_fake_upgrade_release(Dir,"2",Lib2,{[Rel1],[Rel1],[LibDir]}),
Rel1Dir = filename:dirname(Rel1),
Rel2Dir = filename:dirname(Rel2),
@@ -1925,7 +2190,7 @@ unicode_upgrade(Conf) ->
%% Create the releases
RelName = "unicode_rel_αβ",
- Rel1 = create_and_install_fake_first_release(Dir,{RelName,"1"},
+ Rel1 = create_and_install_fake_first_release(Conf,Dir,{RelName,"1"},
[{u,"1.0",LibDir}]),
Rel2 = create_fake_upgrade_release(Dir,
{RelName,"2"},
@@ -2842,9 +3107,9 @@ cover_fun(Node,Func) ->
%% current running OTP release. It includes kernel, stdlib and sasl,
%% and possibly other applications if they are listed in AppDirs =
%% [{App,Vsn,LibDir}]
-create_and_install_fake_first_release(Dir,AppDirs) ->
- create_and_install_fake_first_release(Dir,init:script_id(),AppDirs).
-create_and_install_fake_first_release(Dir,{RelName,RelVsn},AppDirs) ->
+create_and_install_fake_first_release(Conf,Dir,AppDirs) ->
+ create_and_install_fake_first_release(Conf,Dir,init:script_id(),AppDirs).
+create_and_install_fake_first_release(Conf,Dir,{RelName,RelVsn},AppDirs) ->
{Rel,_} = create_fake_release(Dir,RelName,RelVsn,AppDirs),
ReleasesDir = filename:join(Dir, "releases"),
RelDir = filename:dirname(Rel),
@@ -2857,13 +3122,21 @@ create_and_install_fake_first_release(Dir,{RelName,RelVsn},AppDirs) ->
ok = copy_file(Rel++".boot",filename:join(RelVsnDir, "start.boot")),
ok = copy_file(filename:join(RelDir,"sys.config"),RelVsnDir),
- ok = release_handler:create_RELEASES(code:root_dir(),
- ReleasesDir,
- Rel++".rel",
- AppDirs),
-
+ case proplists:get_value(root_dir, Conf, relative) of
+ relative ->
+ ok = release_handler:create_RELEASES(
+ ReleasesDir,
+ Rel++".rel",
+ AppDirs);
+ absolute ->
+ ok = release_handler:create_RELEASES(
+ code:root_dir(),
+ ReleasesDir,
+ Rel++".rel",
+ AppDirs)
+ end,
Rel.
-
+
%% This function create a new release, including a relup file. It can
%% be upgraded to from the release created by
%% create_and_install_fake_first_release/2. Unpack first by calls to
@@ -2958,3 +3231,20 @@ vsn(App,current) ->
system_lib(PrivDir) ->
filename:join(PrivDir,"system_lib").
+
+copy_r(Src, Dst) ->
+ {ok,S} = file:read_file_info(Src),
+ case S#file_info.type of
+ directory ->
+ {ok,Names} = file:list_dir(Src),
+ ok = filelib:ensure_dir(Dst),
+ ok = file:make_dir(Dst),
+ lists:foreach(
+ fun(Name) ->
+ copy_r(filename:join(Src, Name),
+ filename:join(Dst, Name))
+ end, Names);
+ _ ->
+ {ok,_NumBytesCopied} = file:copy(Src, Dst),
+ ok = file:write_file_info(Dst, S)
+ end.
diff --git a/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server-A.rel.src b/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server-A.rel.src
new file mode 100644
index 0000000000..8b8ec2eff6
--- /dev/null
+++ b/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server-A.rel.src
@@ -0,0 +1,8 @@
+{release,
+ {"hello_server", "A"},
+ {erts, "%erts_VSN%"},
+ [{kernel, "%kernel_VSN%"},
+ {stdlib, "%stdlib_VSN%"},
+ {sasl, "%sasl_VSN%"},
+ {hello_server, "A"}]
+}.
diff --git a/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server-B.rel.src b/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server-B.rel.src
new file mode 100644
index 0000000000..c966ad48ec
--- /dev/null
+++ b/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server-B.rel.src
@@ -0,0 +1,8 @@
+{release,
+ {"hello_server", "B"},
+ {erts, "%erts_VSN%"},
+ [{kernel, "%kernel_VSN%"},
+ {stdlib, "%stdlib_VSN%"},
+ {sasl, "%sasl_VSN%"},
+ {hello_server, "B"}]
+}.
diff --git a/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server/Emakefile b/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server/Emakefile
new file mode 100644
index 0000000000..8e1f951aee
--- /dev/null
+++ b/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server/Emakefile
@@ -0,0 +1,2 @@
+{"src/*", [debug_info, {i,"include/"}, {outdir, "ebin/"}]}.
+{"test/*", [debug_info, {i,"include/"}, {outdir, "ebin/"}]}.
diff --git a/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server/ebin/.gitignore b/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server/ebin/.gitignore
new file mode 100644
index 0000000000..1ef2775ff8
--- /dev/null
+++ b/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server/ebin/.gitignore
@@ -0,0 +1 @@
+*.beam \ No newline at end of file
diff --git a/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server/ebin/hello_server.app b/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server/ebin/hello_server.app
new file mode 100644
index 0000000000..697bd7add4
--- /dev/null
+++ b/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server/ebin/hello_server.app
@@ -0,0 +1,7 @@
+{application, hello_server,
+ [{description, "Simple server that sends back hello"},
+ {vsn, "A"},
+ {modules, [app_callback_module, hello_server]},
+ {registered, [hello_server]},
+ {applications, [kernel, stdlib, sasl]},
+ {mod, {app_callback_module,[]}}]}.
diff --git a/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server/src/app_callback_module.erl b/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server/src/app_callback_module.erl
new file mode 100644
index 0000000000..0d988ab8f6
--- /dev/null
+++ b/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server/src/app_callback_module.erl
@@ -0,0 +1,26 @@
+-module(app_callback_module).
+
+-behaviour(application).
+
+-export([start/2, stop/1, get_response/0, update/0]).
+
+start(_Type, _Args) ->
+ Pid = hello_server:start_server(),
+ global:register_name(hello_server, Pid),
+ {ok, Pid}.
+
+update() ->
+ global:whereis_name(hello_server) ! update,
+ ok.
+
+get_response() ->
+ global:whereis_name(hello_server) ! self(),
+ receive
+ A ->
+ A
+ end.
+
+stop(_State) ->
+ Pid = global:whereis_name(hello_server),
+ hello_server:stop(Pid),
+ ok.
diff --git a/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server/src/hello_server.erl b/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server/src/hello_server.erl
new file mode 100644
index 0000000000..1008e1f4e2
--- /dev/null
+++ b/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server/src/hello_server.erl
@@ -0,0 +1,20 @@
+-module(hello_server).
+
+-export([start_server/0, stop/1, loop/0]).
+
+start_server() ->
+ erlang:spawn_link(?MODULE, loop, []).
+
+stop(Pid) ->
+ Pid ! stop.
+
+loop() ->
+ receive
+ stop ->
+ ok;
+ update ->
+ ?MODULE:loop();
+ Sender ->
+ Sender ! hello,
+ loop()
+ end.
diff --git a/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server_new/Emakefile b/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server_new/Emakefile
new file mode 100644
index 0000000000..8e1f951aee
--- /dev/null
+++ b/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server_new/Emakefile
@@ -0,0 +1,2 @@
+{"src/*", [debug_info, {i,"include/"}, {outdir, "ebin/"}]}.
+{"test/*", [debug_info, {i,"include/"}, {outdir, "ebin/"}]}.
diff --git a/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server_new/ebin/.gitignore b/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server_new/ebin/.gitignore
new file mode 100644
index 0000000000..1ef2775ff8
--- /dev/null
+++ b/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server_new/ebin/.gitignore
@@ -0,0 +1 @@
+*.beam \ No newline at end of file
diff --git a/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server_new/ebin/hello_server.app b/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server_new/ebin/hello_server.app
new file mode 100644
index 0000000000..1e1426344b
--- /dev/null
+++ b/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server_new/ebin/hello_server.app
@@ -0,0 +1,7 @@
+{application, hello_server,
+ [{description, "Simple server that sends back hej"},
+ {vsn, "B"},
+ {modules, [app_callback_module, hello_server]},
+ {registered, [hello_server]},
+ {applications, [kernel, stdlib, sasl]},
+ {mod, {app_callback_module,[]}}]}.
diff --git a/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server_new/ebin/hello_server.appup b/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server_new/ebin/hello_server.appup
new file mode 100644
index 0000000000..1aa16cd39c
--- /dev/null
+++ b/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server_new/ebin/hello_server.appup
@@ -0,0 +1,3 @@
+{"B",
+ [{"A", [{load_module, hello_server}]}],
+ [{"A", [{load_module, hello_server}]}]}.
diff --git a/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server_new/src/app_callback_module.erl b/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server_new/src/app_callback_module.erl
new file mode 100644
index 0000000000..0d988ab8f6
--- /dev/null
+++ b/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server_new/src/app_callback_module.erl
@@ -0,0 +1,26 @@
+-module(app_callback_module).
+
+-behaviour(application).
+
+-export([start/2, stop/1, get_response/0, update/0]).
+
+start(_Type, _Args) ->
+ Pid = hello_server:start_server(),
+ global:register_name(hello_server, Pid),
+ {ok, Pid}.
+
+update() ->
+ global:whereis_name(hello_server) ! update,
+ ok.
+
+get_response() ->
+ global:whereis_name(hello_server) ! self(),
+ receive
+ A ->
+ A
+ end.
+
+stop(_State) ->
+ Pid = global:whereis_name(hello_server),
+ hello_server:stop(Pid),
+ ok.
diff --git a/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server_new/src/hello_server.erl b/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server_new/src/hello_server.erl
new file mode 100644
index 0000000000..d40309457a
--- /dev/null
+++ b/lib/sasl/test/release_handler_SUITE_data/relocatable_release/hello_server_new/src/hello_server.erl
@@ -0,0 +1,20 @@
+-module(hello_server).
+
+-export([start_server/0, stop/1, loop/0]).
+
+start_server() ->
+ erlang:spawn_link(?MODULE, loop, []).
+
+stop(Pid) ->
+ Pid ! stop.
+
+loop() ->
+ receive
+ stop ->
+ ok;
+ update ->
+ ?MODULE:loop();
+ Sender ->
+ Sender ! hej,
+ loop()
+ end.