檢視原始碼 建立及升級目標系統

當使用 Erlang/OTP 建立系統時,最簡單的方法是在某處安裝 Erlang/OTP,在其他地方安裝特定應用程式的程式碼,然後啟動 Erlang 運行時系統,確保程式碼路徑包含特定應用程式的程式碼。

通常不希望直接使用 Erlang/OTP 系統。開發人員可以為特定目的建立新的符合 Erlang/OTP 標準的應用程式,而幾個原始 Erlang/OTP 應用程式可能與所考慮的目的無關。因此,需要能夠根據給定的 Erlang/OTP 系統建立新的系統,其中移除不必要的應用程式並包含新的應用程式。文件和原始碼無關,因此不包含在新系統中。

本章節是關於建立這樣的系統,稱為目標系統

以下章節將介紹對功能有不同要求的目標系統

  • 一個基本目標系統,可以透過呼叫普通的 erl 腳本啟動。
  • 一個簡單目標系統,也支援執行時的程式碼替換。
  • 一個嵌入式目標系統,也支援在開機時自動啟動,並記錄系統檔案的輸出以供稍後檢查。

這裡僅考慮 Erlang/OTP 在 UNIX 系統上執行的情況。

sasl 應用程式包含範例 Erlang 模組 target_system.erl,其中包含用於建立和安裝目標系統的函數。此模組將在以下範例中使用。該模組的原始碼列在 target_system.erl 的程式碼清單

建立目標系統

假設您有一個依照 OTP 設計原則建構的正常運作的 Erlang/OTP 系統。

步驟 1. 建立一個 .rel 檔案(請參閱 SASL 中的 rel(4) 手冊頁面),其中指定 ERTS 版本並列出要包含在新基本目標系統中的所有應用程式。以下是一個 mysystem.rel 檔案的範例

%% mysystem.rel
{release,
 {"MYSYSTEM", "FIRST"},
 {erts, "5.10.4"},
 [{kernel, "2.16.4"},
  {stdlib, "1.19.4"},
  {sasl, "2.3.4"},
  {pea, "1.0"}]}.

列出的應用程式不僅是原始的 Erlang/OTP 應用程式,也可能是您編寫的新應用程式(這裡以應用程式 Pea (pea) 為例)。

步驟 2.mysystem.rel 檔案所在的目錄啟動 Erlang/OTP

% erl -pa /home/user/target_system/myapps/pea-1.0/ebin

-pa 引數將 Pea 應用程式的 ebin 目錄的路徑加到程式碼路徑的前面。

步驟 3. 建立目標系統

1> target_system:create("mysystem").

函數 target_system:create/1 執行以下操作

  1. 讀取檔案 mysystem.rel 並建立一個新的檔案 plain.rel。新檔案與原始檔案相同,只是它只列出 Kernel 和 STDLIB 應用程式。

  2. 透過呼叫 systools:make_script/2,從檔案 mysystem.relplain.rel 建立檔案 mysystem.scriptmysystem.bootplain.scriptplain.boot

  3. 透過呼叫 systools:make_tar/2 建立檔案 mysystem.tar.gz。該檔案包含以下內容

erts-5.10.4/bin/
releases/FIRST/start.boot
releases/FIRST/mysystem.rel
releases/mysystem.rel
lib/kernel-2.16.4/
lib/stdlib-1.19.4/
lib/sasl-2.3.4/
lib/pea-1.0/

檔案 releases/FIRST/start.boot 是我們 mysystem.boot 的副本

發布資源檔案 mysystem.rel 在 tar 檔案中重複。最初,此檔案僅儲存在 releases 目錄中,以使 release_handler 能夠單獨提取此檔案。解壓縮 tar 檔案後,release_handler 會自動將檔案複製到 releases/FIRST。但是,有時會在不涉及 release_handler 的情況下解壓縮 tar 檔案(例如,解壓縮第一個目標系統時)。因此,該檔案現在在 tar 封存檔中重複,從而無需手動複製。

  1. 建立臨時目錄 tmp,並將 tar 檔案 mysystem.tar.gz 解壓縮到該目錄中。
  2. tmp/erts-5.10.4/bin 中刪除檔案 erlstart。這些檔案將在安裝發布版本時從原始碼重新建立。
  3. 建立目錄 tmp/bin
  4. 將先前建立的檔案 plain.boot 複製到 tmp/bin/start.boot
  5. 將檔案 epmdrun_erlto_erl 從目錄 tmp/erts-5.10.4/bin 複製到目錄 tmp/bin
  6. 建立目錄 tmp/log,如果系統使用 bin/start 腳本以嵌入式方式啟動,則會使用此目錄。
  7. 建立檔案 tmp/releases/start_erl.data,其中包含 "5.10.4 FIRST" 的內容。此檔案將作為資料檔案傳遞給 start_erl 腳本。
  8. 從目錄 tmp 中的目錄重新建立檔案 mysystem.tar.gz,並刪除 tmp

安裝目標系統

步驟 4. 將建立的目標系統安裝到合適的目錄中。

2> target_system:install("mysystem", "/usr/local/erl-target").

函數 target_system:install/2 執行以下操作

  1. 將 tar 檔案 mysystem.tar.gz 解壓縮到目標目錄 /usr/local/erl-target
  2. 在目標目錄中讀取檔案 releases/start_erl.data 以查找 Erlang 執行時系統版本 ("5.10.4")。
  3. 將目標 erts-5.10.4/bin 目錄中的檔案 erl.srcstart.srcstart_erl.src 中的 %FINAL_ROOTDIR%%EMU% 分別替換為 /usr/local/erl-targetbeam,並將結果檔案 erlstartrun_erl 放置在目標 bin 目錄中。
  4. 最後,從檔案 releases/mysystem.rel 中的資料建立目標 releases/RELEASES 檔案。

啟動目標系統

現在我們有一個可以透過各種方式啟動的目標系統。我們透過調用以下命令,將其作為基本目標系統啟動

% /usr/local/erl-target/bin/erl

這裡只啟動 Kernel 和 STDLIB 應用程式,也就是說,該系統是作為普通的開發系統啟動的。要使所有這些都正常運作,只需要兩個檔案

  1. bin/erl(從 erts-5.10.4/bin/erl.src 取得)
  2. bin/start.bootplain.boot 的副本)

我們也可以啟動分散式系統(需要 bin/epmd)。

若要啟動原始 mysystem.rel 檔案中指定的所有應用程式,請使用以下 -boot 旗標

% /usr/local/erl-target/bin/erl -boot /usr/local/erl-target/releases/FIRST/start

我們如上所述啟動簡單目標系統。唯一的區別是,releases/RELEASES 檔案也存在,以便在執行時進行程式碼替換。

若要啟動嵌入式目標系統,請使用 shell 腳本 bin/start。該腳本呼叫 bin/run_erl,而 bin/run_erl 又呼叫 bin/start_erl(粗略來說,start_erlerl 的嵌入式變體)。

在安裝期間從 erts-5.10.4/bin/start.src 產生的 shell 腳本 start 只是一個範例。請根據您的需要編輯它。通常,它會在 UNIX 系統啟動時執行。

run_erl 是一個包裝函式,它提供將執行時系統的輸出記錄到檔案中。它還提供一種簡單的機制來附加到 Erlang shell (to_erl)。

start_erl 需要

  1. 根目錄 ("/usr/local/erl-target")
  2. 發布版本目錄 ("/usr/local/erl-target/releases")
  3. start_erl.data 檔案的位置

它執行以下操作

  1. 從檔案 start_erl.data 中讀取執行時系統版本 ("5.10.4") 和發布版本 ("FIRST")。
  2. 啟動找到的版本的執行時系統。
  3. 提供旗標 -boot,指定找到的發布版本的啟動檔案 ("releases/FIRST/start.boot")。

start_erl 還假設發布版本目錄中存在 sys.config ("releases/FIRST/sys.config")。這是下一節的主題。

start_erl shell 腳本通常不應由使用者更改。

系統組態參數

如上一節所述,start_erl 需要發布版本目錄中存在 sys.config ("releases/FIRST/sys.config")。如果沒有此檔案,系統啟動會失敗。因此,也必須加入此類檔案。

如果您的系統組態資料既不依賴於檔案位置也不依賴於站點,則盡早建立 sys.config 會很方便,這樣它就會成為 target_system:create/1 建立的目標系統 tar 檔案的一部分。事實上,如果您在目前目錄中不僅建立檔案 mysystem.rel,還建立檔案 sys.config,則後者檔案會被悄悄地放入適當的目錄中。

但是,在解壓縮後但在執行發布版本之前,在目標上的 sys.config 中替換變數也可能會很方便。如果您有 sys.config.src,它將被包含,並且不需要像 sys.config 那樣成為有效的 Erlang 術語檔案。在執行發布版本之前,您必須在同一個目錄中有一個有效的 sys.config,因此使用 sys.config.src 需要有一些工具來填入所需的內容,並在啟動發布版本之前將 sys.config 寫入磁碟。

與安裝腳本的差異

先前的 install/2 程序與普通的 Install shell 腳本的程序略有不同。事實上,create/1 會使發布版本套件盡可能完整,並留給 install/2 程序來完成,方法是僅考慮與位置相關的檔案。

建立下一個版本

在此範例中,Pea 應用程式已變更,ERTS、Kernel、STDLIB 和 SASL 應用程式也是如此。

步驟 1. 建立檔案 .rel

%% mysystem2.rel
{release,
 {"MYSYSTEM", "SECOND"},
 {erts, "6.0"},
 [{kernel, "3.0"},
  {stdlib, "2.0"},
  {sasl, "2.4"},
  {pea, "2.0"}]}.

步驟 2. 為 Pea 建立應用程式升級檔案(請參閱 SASL 中的 appup),例如

%% pea.appup
{"2.0",
 [{"1.0",[{load_module,pea_lib}]}],
 [{"1.0",[{load_module,pea_lib}]}]}.

步驟 3. 從檔案 mysystem2.rel 所在的目錄啟動 Erlang/OTP 系統,並提供新版本 Pea 的路徑

% erl -pa /home/user/target_system/myapps/pea-2.0/ebin

步驟 4. 建立發布版本升級檔案(請參閱 SASL 中的 relup

1> systools:make_relup("mysystem2",["mysystem"],["mysystem"],
    [{path,["/home/user/target_system/myapps/pea-1.0/ebin",
    "/my/old/erlang/lib/*/ebin"]}]).

這裡,"mysystem" 是基本發布版本,"mysystem2" 是要升級到的發布版本。

path 選項用於指出所有應用程式的舊版本。(新版本已在程式碼路徑中 - 當然前提是執行此操作的 Erlang 節點正在執行正確版本的 Erlang/OTP。)

步驟 5. 建立新的發布版本

2> target_system:create("mysystem2").

假設步驟 4 中產生的檔案 relup 現在位於目前目錄,它將會自動包含在發布套件中。

升級目標系統

這個部分在目標節點上完成。在這個範例中,我們希望節點以嵌入式系統的方式執行,並使用 -heart 選項,以便自動重新啟動節點。有關更多資訊,請參閱啟動目標系統

我們將 -heart 加入到 bin/start 中。

#!/bin/sh
ROOTDIR=/usr/local/erl-target/

if [ -z "$RELDIR" ]
then
   RELDIR=$ROOTDIR/releases
fi

START_ERL_DATA=${1:-$RELDIR/start_erl.data}

$ROOTDIR/bin/run_erl -daemon /tmp/ $ROOTDIR/log "exec $ROOTDIR/bin/start_erl $ROOTDIR\
$RELDIR $START_ERL_DATA -heart"

我們使用最簡單的 sys.config,並將其儲存在 releases/FIRST 中。

%% sys.config
[].

最後,為了準備升級,我們必須將新的發布套件放入第一個目標系統的 releases 目錄中。

% cp mysystem2.tar.gz /usr/local/erl-target/releases

假設節點已按照以下方式啟動

% /usr/local/erl-target/bin/start

它可以使用以下方式存取

% /usr/local/erl-target/bin/to_erl /tmp/erlang.pipe.1

日誌可以在 /usr/local/erl-target/log 中找到。此目錄是在啟動腳本中,作為 run_erl 的參數指定。

步驟 1. 解壓縮發布套件

1> {ok,Vsn} = release_handler:unpack_release("mysystem2").

步驟 2. 安裝發布套件

2> release_handler:install_release(Vsn).
{continue_after_restart,"FIRST",[]}
heart: Tue Apr  1 12:15:10 2014: Erlang has closed.
heart: Tue Apr  1 12:15:11 2014: Executed "/usr/local/erl-target/bin/start /usr/local/erl-target/releases/new_start_erl.data" -> 0. Terminating.
[End]

在呼叫 release_handler:install_release/1 之後,上述回傳值和輸出表示 release_handler 已使用 heart 重新啟動節點。當升級涉及應用程式 ERTS、Kernel、STDLIB 或 SASL 的變更時,始終會執行此操作。有關更多資訊,請參閱當 Erlang/OTP 變更時進行升級

現在可以透過新的管道存取節點

% /usr/local/erl-target/bin/to_erl /tmp/erlang.pipe.2

列出系統中可用的發布套件

1> release_handler:which_releases().
[{"MYSYSTEM","SECOND",
  ["kernel-3.0","stdlib-2.0","sasl-2.4","pea-2.0"],
  current},
 {"MYSYSTEM","FIRST",
  ["kernel-2.16.4","stdlib-1.19.4","sasl-2.3.4","pea-1.0"],
  permanent}]

我們的新發布套件 "SECOND" 現在是目前的發布套件,但我們也可以看到我們的 "FIRST" 發布套件仍然是永久的。這表示如果現在重新啟動節點,它將會再次執行 "FIRST" 發布套件。

步驟 3. 將新的發布套件設為永久

2> release_handler:make_permanent("SECOND").

再次檢查發布套件

3> release_handler:which_releases().
[{"MYSYSTEM","SECOND",
  ["kernel-3.0","stdlib-2.0","sasl-2.4","pea-2.0"],
  permanent},
 {"MYSYSTEM","FIRST",
  ["kernel-2.16.4","stdlib-1.19.4","sasl-2.3.4","pea-1.0"],
  old}]

我們看到新的發布套件版本是 permanent,因此重新啟動節點會是安全的。

target_system.erl 的列表

這個模組也可以在 SASL 應用程式的 examples 目錄中找到。


-module(target_system).
-export([create/1, create/2, install/2]).

%% Note: RelFileName below is the *stem* without trailing .rel,
%% .script etc.
%%

%% create(RelFileName)
%%
create(RelFileName) ->
    create(RelFileName,[]).

create(RelFileName,SystoolsOpts) ->
    RelFile = RelFileName ++ ".rel",
    Dir = filename:dirname(RelFileName),
    PlainRelFileName = filename:join(Dir,"plain"),
    PlainRelFile = PlainRelFileName ++ ".rel",
    io:fwrite("Reading file: ~ts ...~n", [RelFile]),
    {ok, [RelSpec]} = file:consult(RelFile),
    io:fwrite("Creating file: ~ts from ~ts ...~n",
              [PlainRelFile, 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(PlainRelFile, [write]),
    io:fwrite(Fd, "~p.~n", [PlainRelSpec]),
    file:close(Fd),

    io:fwrite("Making \"~ts.script\" and \"~ts.boot\" files ...~n",
	      [PlainRelFileName,PlainRelFileName]),
    make_script(PlainRelFileName,SystoolsOpts),

    io:fwrite("Making \"~ts.script\" and \"~ts.boot\" files ...~n",
              [RelFileName, RelFileName]),
    make_script(RelFileName,SystoolsOpts),

    TarFileName = RelFileName ++ ".tar.gz",
    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 ~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 ~ts ...~n",
              [ErtsBinDir]),
    file:delete(filename:join([ErtsBinDir, "erl"])),
    file:delete(filename:join([ErtsBinDir, "start"])),

    io:fwrite("Creating temporary directory ~ts ...~n", [TmpBinDir]),
    file:make_dir(TmpBinDir),

    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"
              "~ts to ~ts ...~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]),

    %% 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 ~ts ...~n", [TmpLogDir]),
    ok = file:make_dir(TmpLogDir),

    StartErlDataFile = filename:join([TmpDir, "releases", "start_erl.data"]),
    io:fwrite("Creating ~ts ...~n", [StartErlDataFile]),
    StartErlData = io_lib:fwrite("~s ~s~n", [ErtsVsn, RelVsn]),
    write_file(StartErlDataFile, StartErlData),

    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(),
    %% file:set_cwd("tmp"),
    ErtsDir = "erts-"++ErtsVsn,
    erl_tar:add(Tar, filename:join(TmpDir,"bin"), "bin", []),
    erl_tar:add(Tar, filename:join(TmpDir,ErtsDir), ErtsDir, []),
    erl_tar:add(Tar, filename:join(TmpDir,"releases"), "releases", []),
    erl_tar:add(Tar, filename:join(TmpDir,"lib"), "lib", []),
    erl_tar:add(Tar, filename:join(TmpDir,"log"), "log", []),
    erl_tar:close(Tar),
    %% file:set_cwd(Cwd),
    io:fwrite("Removing directory ~ts ...~n",[TmpDir]),
    remove_dir_tree(TmpDir),
    ok.


install(RelFileName, RootDir) ->
    TarFile = RelFileName ++ ".tar.gz",
    io:fwrite("Extracting ~ts ...~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 "
              "form erl, start and start_erl ...\n"),
    subst_src_scripts(["erl", "start", "start_erl"], ErtsBinDir, BinDir,
                      [{"FINAL_ROOTDIR", RootDir}, {"EMU", "beam"}],
                      [preserve]),
    %%! Workaround for pre OTP 17.0: start.src and start_erl.src did
    %%! not have correct permissions, so the above 'preserve' option did not help
    ok = file:change_mode(filename:join(BinDir,"start"),8#0755),
    ok = file:change_mode(filename:join(BinDir,"start_erl"),8#0755),

    io:fwrite("Creating the RELEASES file ...\n"),
    create_RELEASES(RootDir, filename:join([RootDir, "releases",
					    filename:basename(RelFileName)])).

%% LOCALS

%% make_script(RelFileName,Opts)
%%
make_script(RelFileName,Opts) ->
    systools:make_script(RelFileName, [no_module_tests,
				       {outdir,filename:dirname(RelFileName)}
				       |Opts]).

%% make_tar(RelFileName,Opts)
%%
make_tar(RelFileName,Opts) ->
    RootDir = code:root_dir(),
    systools:make_tar(RelFileName, [{erts, RootDir},
				    {outdir,filename:dirname(RelFileName)}
				    |Opts]).

%% 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,_} = file:copy(Src, Dest),
    case lists:member(preserve, Opts) of
        true ->
            {ok, FileInfo} = file:read_file_info(Src),
            file:write_file_info(Dest, FileInfo);
        false ->
            ok
    end.

write_file(FName, Conts) ->
    Enc = file:native_name_encoding(),
    {ok, Fd} = file:open(FName, [write]),
    file:write(Fd, unicode:characters_to_binary(Conts,Enc,Enc)),
    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]),
                          case filelib:is_dir(FilePath) of
                              true ->
                                  {ok, DirFiles} = file:list_dir(FilePath),
                                  remove_all_files(FilePath, DirFiles),
                                  file:del_dir(FilePath);
                              _ ->
                                  file:delete(FilePath)
                          end
                  end, Files).