When creating a system using Erlang/OTP, the most simple way is to install Erlang/OTP somewhere, install the application specific code somewhere else, and then start the Erlang runtime system, making sure the code path includes the application specific code.
Often it is not desirable to use an Erlang/OTP system as is. A developer may create new Erlang/OTP compliant applications for a particular purpose, and several original Erlang/OTP applications may be irrelevant for the purpose in question. Thus, there is a need to be able to create a new system based on a given Erlang/OTP system, where dispensable applications are removed, and a set of new applications that are included in the new system. Documentation and source code is irrelevant and is therefore not included in the new system.
This chapter is about creating such a system, which we call a target system.
In the following sections we consider creating target systems with different requirements of functionality:
We only consider the case when Erlang/OTP is running on a UNIX system.
There is an example Erlang module
It is assumed that you have a working Erlang/OTP system structured according to the OTP Design Principles.
Step 1. First create a
%% mysystem.rel
{release,
{"MYSYSTEM", "FIRST"},
{erts, "5.1"},
[{kernel, "2.7"},
{stdlib, "1.10"},
{sasl, "1.9.3"},
{pea, "1.0"}]}.
The listed applications are not only original Erlang/OTP
applications but possibly also new applications that you have
written yourself (here examplified by the application
Step 2. From the directory where the
os> erl -pa /home/user/target_system/myapps/pea-1.0/ebin
where also the path to the
Step 3. Now create the target system:
1> target_system:create("mysystem").
The
Creates the file
erts-5.1/bin/
releases/FIRST/start.boot
releases/mysystem.rel
lib/kernel-2.7/
lib/stdlib-1.10/
lib/sasl-1.9.3/
lib/pea-1.0/
The file
Step 4. Install the created target system in a suitable directory.
2> target_system:install("mysystem", "/usr/local/erl-target").
The function
Now we have a target system that can be started in various ways.
We start it as a basic target system by invoking
os> /usr/local/erl-target/bin/erl
where only the
We can also start a distributed system (requires
To start all applications specified in the original
os> /usr/local/erl-target/bin/erl -boot /usr/local/erl-target/releases/FIRST/start
We start a simple target system as above. The only difference
is that also the file
To start an embedded target system the shell script
The shell script
The
As was pointed out above
If you have system configuration data that are neither file
location dependent nor site dependent, it may be convenient to
create the
The above
RelFile = RelFileName ++ ".rel",
io:fwrite("Reading file: \"~s\" ...~n", [RelFile]),
{ok, [RelSpec]} = file:consult(RelFile),
io:fwrite("Creating file: \"~s\" from \"~s\" ...~n",
["plain.rel", RelFile]),
{release,
{RelName, RelVsn},
{erts, ErtsVsn},
AppVsns} = RelSpec,
PlainRelSpec = {release,
{RelName, RelVsn},
{erts, ErtsVsn},
lists:filter(fun({kernel, _}) ->
true;
({stdlib, _}) ->
true;
(_) ->
false
end, AppVsns)
},
{ok, Fd} = file:open("plain.rel", [write]),
io:fwrite(Fd, "~p.~n", [PlainRelSpec]),
file:close(Fd),
io:fwrite("Making \"plain.script\" and \"plain.boot\" files ...~n"),
make_script("plain"),
io:fwrite("Making \"~s.script\" and \"~s.boot\" files ...~n",
[RelFileName, RelFileName]),
make_script(RelFileName),
TarFileName = io_lib:fwrite("~s.tar.gz", [RelFileName]),
io:fwrite("Creating tar file \"~s\" ...~n", [TarFileName]),
make_tar(RelFileName),
io:fwrite("Creating directory \"tmp\" ...~n"),
file:make_dir("tmp"),
io:fwrite("Extracting \"~s\" into directory \"tmp\" ...~n", [TarFileName]),
extract_tar(TarFileName, "tmp"),
TmpBinDir = filename:join(["tmp", "bin"]),
ErtsBinDir = filename:join(["tmp", "erts-" ++ ErtsVsn, "bin"]),
io:fwrite("Deleting \"erl\" and \"start\" in directory \"~s\" ...~n",
[ErtsBinDir]),
file:delete(filename:join([ErtsBinDir, "erl"])),
file:delete(filename:join([ErtsBinDir, "start"])),
io:fwrite("Creating temporary directory \"~s\" ...~n", [TmpBinDir]),
file:make_dir(TmpBinDir),
io:fwrite("Copying file \"plain.boot\" to \"~s\" ...~n",
[filename:join([TmpBinDir, "start.boot"])]),
copy_file("plain.boot", filename:join([TmpBinDir, "start.boot"])),
io:fwrite("Copying files \"epmd\", \"run_erl\" and \"to_erl\" from \n"
"\"~s\" to \"~s\" ...~n",
[ErtsBinDir, TmpBinDir]),
copy_file(filename:join([ErtsBinDir, "epmd"]),
filename:join([TmpBinDir, "epmd"]), [preserve]),
copy_file(filename:join([ErtsBinDir, "run_erl"]),
filename:join([TmpBinDir, "run_erl"]), [preserve]),
copy_file(filename:join([ErtsBinDir, "to_erl"]),
filename:join([TmpBinDir, "to_erl"]), [preserve]),
StartErlDataFile = filename:join(["tmp", "releases", "start_erl.data"]),
io:fwrite("Creating \"~s\" ...~n", [StartErlDataFile]),
StartErlData = io_lib:fwrite("~s ~s~n", [ErtsVsn, RelVsn]),
write_file(StartErlDataFile, StartErlData),
io:fwrite("Recreating tar file \"~s\" from contents in directory "
"\"tmp\" ...~n", [TarFileName]),
{ok, Tar} = erl_tar:open(TarFileName, [write, compressed]),
{ok, Cwd} = file:get_cwd(),
file:set_cwd("tmp"),
erl_tar:add(Tar, "bin", []),
erl_tar:add(Tar, "erts-" ++ ErtsVsn, []),
erl_tar:add(Tar, "releases", []),
erl_tar:add(Tar, "lib", []),
erl_tar:close(Tar),
file:set_cwd(Cwd),
io:fwrite("Removing directory \"tmp\" ...~n"),
remove_dir_tree("tmp"),
ok.
install(RelFileName, RootDir) ->
TarFile = RelFileName ++ ".tar.gz",
io:fwrite("Extracting ~s ...~n", [TarFile]),
extract_tar(TarFile, RootDir),
StartErlDataFile = filename:join([RootDir, "releases", "start_erl.data"]),
{ok, StartErlData} = read_txt_file(StartErlDataFile),
[ErlVsn, RelVsn| _] = string:tokens(StartErlData, " \n"),
ErtsBinDir = filename:join([RootDir, "erts-" ++ ErlVsn, "bin"]),
BinDir = filename:join([RootDir, "bin"]),
io:fwrite("Substituting in erl.src, start.src and start_erl.src to\n"
"form erl, start and start_erl ...\n"),
subst_src_scripts(["erl", "start", "start_erl"], ErtsBinDir, BinDir,
[{"FINAL_ROOTDIR", RootDir}, {"EMU", "beam"}],
[preserve]),
io:fwrite("Creating the RELEASES file ...\n"),
create_RELEASES(RootDir,
filename:join([RootDir, "releases", RelFileName])).
%% LOCALS
%% make_script(RelFileName)
%%
make_script(RelFileName) ->
Opts = [no_module_tests],
systools:make_script(RelFileName, Opts).
%% make_tar(RelFileName)
%%
make_tar(RelFileName) ->
RootDir = code:root_dir(),
systools:make_tar(RelFileName, [{erts, RootDir}]).
%% extract_tar(TarFile, DestDir)
%%
extract_tar(TarFile, DestDir) ->
erl_tar:extract(TarFile, [{cwd, DestDir}, compressed]).
create_RELEASES(DestDir, RelFileName) ->
release_handler:create_RELEASES(DestDir, RelFileName ++ ".rel").
subst_src_scripts(Scripts, SrcDir, DestDir, Vars, Opts) ->
lists:foreach(fun(Script) ->
subst_src_script(Script, SrcDir, DestDir,
Vars, Opts)
end, Scripts).
subst_src_script(Script, SrcDir, DestDir, Vars, Opts) ->
subst_file(filename:join([SrcDir, Script ++ ".src"]),
filename:join([DestDir, Script]),
Vars, Opts).
subst_file(Src, Dest, Vars, Opts) ->
{ok, Conts} = read_txt_file(Src),
NConts = subst(Conts, Vars),
write_file(Dest, NConts),
case lists:member(preserve, Opts) of
true ->
{ok, FileInfo} = file:read_file_info(Src),
file:write_file_info(Dest, FileInfo);
false ->
ok
end.
%% subst(Str, Vars)
%% Vars = [{Var, Val}]
%% Var = Val = string()
%% Substitute all occurrences of %Var% for Val in Str, using the list
%% of variables in Vars.
%%
subst(Str, Vars) ->
subst(Str, Vars, []).
subst([$%, C| Rest], Vars, Result) when $A =< C, C =< $Z ->
subst_var([C| Rest], Vars, Result, []);
subst([$%, C| Rest], Vars, Result) when $a =< C, C =< $z ->
subst_var([C| Rest], Vars, Result, []);
subst([$%, C| Rest], Vars, Result) when C == $_ ->
subst_var([C| Rest], Vars, Result, []);
subst([C| Rest], Vars, Result) ->
subst(Rest, Vars, [C| Result]);
subst([], _Vars, Result) ->
lists:reverse(Result).
subst_var([$%| Rest], Vars, Result, VarAcc) ->
Key = lists:reverse(VarAcc),
case lists:keysearch(Key, 1, Vars) of
{value, {Key, Value}} ->
subst(Rest, Vars, lists:reverse(Value, Result));
false ->
subst(Rest, Vars, [$%| VarAcc ++ [$%| Result]])
end;
subst_var([C| Rest], Vars, Result, VarAcc) ->
subst_var(Rest, Vars, Result, [C| VarAcc]);
subst_var([], Vars, Result, VarAcc) ->
subst([], Vars, [VarAcc ++ [$%| Result]]).
copy_file(Src, Dest) ->
copy_file(Src, Dest, []).
copy_file(Src, Dest, Opts) ->
{ok, InFd} = file:open(Src, [raw, binary, read]),
{ok, OutFd} = file:open(Dest, [raw, binary, write]),
do_copy_file(InFd, OutFd),
file:close(InFd),
file:close(OutFd),
case lists:member(preserve, Opts) of
true ->
{ok, FileInfo} = file:read_file_info(Src),
file:write_file_info(Dest, FileInfo);
false ->
ok
end.
do_copy_file(InFd, OutFd) ->
case file:read(InFd, ?BUFSIZE) of
{ok, Bin} ->
file:write(OutFd, Bin),
do_copy_file(InFd, OutFd);
eof ->
ok
end.
write_file(FName, Conts) ->
{ok, Fd} = file:open(FName, [write]),
file:write(Fd, Conts),
file:close(Fd).
read_txt_file(File) ->
{ok, Bin} = file:read_file(File),
{ok, binary_to_list(Bin)}.
remove_dir_tree(Dir) ->
remove_all_files(".", [Dir]).
remove_all_files(Dir, Files) ->
lists:foreach(fun(File) ->
FilePath = filename:join([Dir, File]),
{ok, FileInfo} = file:read_file_info(FilePath),
case FileInfo#file_info.type of
directory ->
{ok, DirFiles} = file:list_dir(FilePath),
remove_all_files(FilePath, DirFiles),
file:del_dir(FilePath);
_ ->
file:delete(FilePath)
end
end, Files).
]]>