aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorTristan Sloughter <[email protected]>2013-09-09 20:16:55 -0500
committerTristan Sloughter <[email protected]>2013-09-11 20:07:33 -0500
commitff0a11dc7b2e2ef902c97a62595b29e9bc89029a (patch)
treea96c523adba91117ee3ce4cfa557155c0cc303db /src
parent2f54b2d5360c52fff2c3f8fde73b29b6069174ea (diff)
downloadrelx-ff0a11dc7b2e2ef902c97a62595b29e9bc89029a.tar.gz
relx-ff0a11dc7b2e2ef902c97a62595b29e9bc89029a.tar.bz2
relx-ff0a11dc7b2e2ef902c97a62595b29e9bc89029a.zip
allow multiple actions, including new action tar
Diffstat (limited to 'src')
-rw-r--r--src/relx.erl12
-rw-r--r--src/rlx_cmd_args.erl43
-rw-r--r--src/rlx_prv_assembler.erl196
-rw-r--r--src/rlx_prv_release.erl17
-rw-r--r--src/rlx_state.erl28
-rw-r--r--src/rlx_util.erl16
6 files changed, 239 insertions, 73 deletions
diff --git a/src/relx.erl b/src/relx.erl
index 30ee77f..7419352 100644
--- a/src/relx.erl
+++ b/src/relx.erl
@@ -156,12 +156,12 @@ do(RootDir, RelName, RelVsn, Goals, LibDirs, LogLevel, OutputDir, Overrides, Con
-spec do(proplists:proplist(), [string()]) ->
ok | error() | {ok, rlx_state:t()}.
do(Opts, NonOpts) ->
- case rlx_cmd_args:args2state(Opts, NonOpts) of
- {ok, State} ->
- run_relx_process(State);
- Error={error, _} ->
- Error
- end.
+ case rlx_cmd_args:args2state(Opts, NonOpts) of
+ {ok, State} ->
+ run_relx_process(State);
+ Error={error, _} ->
+ Error
+ end.
-spec opt_spec_list() -> [getopt:option_spec()].
opt_spec_list() ->
diff --git a/src/rlx_cmd_args.erl b/src/rlx_cmd_args.erl
index 1a8e7f2..4fbadba 100644
--- a/src/rlx_cmd_args.erl
+++ b/src/rlx_cmd_args.erl
@@ -32,24 +32,21 @@
-spec args2state([getopt:option()], [string()]) ->
{ok, {rlx_state:t(), [string()]}} |
relx:error().
-args2state(Opts, Target)
- when erlang:length(Target) == 0; erlang:length(Target) == 1 ->
+args2state(Opts, Targets) ->
RelName = rlx_util:to_atom(proplists:get_value(relname, Opts, undefined)),
RelVsn = proplists:get_value(relvsn, Opts, undefined),
- case convert_target(Target) of
- {ok, AtomizedTarget} ->
+ case convert_targets(Targets) of
+ {ok, AtomizedTargets} ->
case create_log(Opts, [{relname, RelName},
{relvsn, RelVsn}]) of
Error = {error, _} ->
Error;
{ok, CommandLineConfig} ->
- handle_config(Opts, AtomizedTarget, CommandLineConfig)
+ handle_config(Opts, AtomizedTargets, CommandLineConfig)
end;
Error ->
Error
- end;
-args2state(_Opts, Targets) ->
- ?RLX_ERROR({invalid_targets, Targets}).
+ end.
-spec format_error(Reason::term()) -> iolist().
format_error({invalid_targets, Targets}) ->
@@ -88,25 +85,33 @@ format_error({invalid_target, Target}) ->
%%%===================================================================
%%% Internal Functions
%%%===================================================================
--spec handle_config([getopt:option()], atom(), proplists:proplist()) ->
+-spec handle_config([getopt:option()], [atom()], proplists:proplist()) ->
{ok, {rlx_state:t(), [string()]}} |
relx:error().
-handle_config(Opts, Target, CommandLineConfig) ->
+handle_config(Opts, Targets, CommandLineConfig) ->
case validate_config(proplists:get_value(config, Opts, [])) of
Error = {error, _} ->
Error;
{ok, Config} ->
- {ok, rlx_state:new([{config, Config} | CommandLineConfig], Target)}
+ {ok, rlx_state:new([{config, Config} | CommandLineConfig], Targets)}
end.
--spec convert_target([string()]) -> {ok, release | relup} | relx:error().
-convert_target([]) ->
- {ok, release};
-convert_target(["release"]) ->
- {ok, release};
-convert_target(["relup"]) ->
- {ok, relup};
-convert_target(Target) ->
+-spec convert_targets([string()]) -> {ok, release | relup} | relx:error().
+convert_targets(Targets) ->
+ convert_targets(Targets, []).
+
+-spec convert_targets([string()], [string()]) -> {ok, release | relup} | relx:error().
+convert_targets([], []) ->
+ {ok, [release]};
+convert_targets([], Acc) ->
+ {ok, Acc};
+convert_targets(["release" | T], Acc) ->
+ convert_targets(T, [release | Acc]);
+convert_targets(["relup" | T], Acc) ->
+ convert_targets(T, [relup | Acc]);
+convert_targets(["tar" | T], Acc) ->
+ convert_targets(T, [tar | Acc]);
+convert_targets([Target | _T], _Acc) ->
?RLX_ERROR({invalid_target, Target}).
-spec validate_config(file:filename() | undefined) ->
diff --git a/src/rlx_prv_assembler.erl b/src/rlx_prv_assembler.erl
index b2f6451..db5d147 100644
--- a/src/rlx_prv_assembler.erl
+++ b/src/rlx_prv_assembler.erl
@@ -48,7 +48,7 @@ do(State) ->
ok ->
case rlx_release:realized(Release) of
true ->
- copy_app_directories_to_output(State, Release, OutputDir);
+ run_actions(State, Release, OutputDir);
false ->
?RLX_ERROR({unresolved_release, RelName, RelVsn})
end;
@@ -56,6 +56,34 @@ do(State) ->
Error
end.
+do(release, State, Release, OutputDir) ->
+ copy_app_directories_to_output(State, Release, OutputDir);
+do(relup, State, Release, _OutputDir) ->
+ RelName = rlx_release:name(Release),
+ RelVsn = rlx_release:vsn(Release),
+ Release0 = rlx_state:get_realized_release(State, RelName, RelVsn),
+ make_relup(State, Release0);
+do(tar, State, Release, OutputDir) ->
+ make_tar(State, Release, OutputDir).
+
+run_actions(State, Release, OutputDir) ->
+ run_actions(State, Release, OutputDir, rlx_state:actions(State), [release, relup, tar]).
+
+run_actions(State, _Release, _OutputDir, _Actions, []) ->
+ {ok, State};
+run_actions(State, Release, OutputDir, Actions, [H | T]) ->
+ case lists:member(H, Actions) of
+ true ->
+ case do(H, State, Release, OutputDir) of
+ {ok, NewState} ->
+ run_actions(NewState, Release, OutputDir, Actions, T);
+ Error ->
+ Error
+ end;
+ false ->
+ run_actions(State, Release, OutputDir, Actions, T)
+ end.
+
-spec format_error(ErrorDetail::term()) -> iolist().
format_error({unresolved_release, RelName, RelVsn}) ->
io_lib:format("The release has not been resolved ~p-~s", [RelName, RelVsn]);
@@ -97,7 +125,16 @@ format_error({relup_script_generation_error, Module, Errors}) ->
format_error({unable_to_make_symlink, AppDir, TargetDir, Reason}) ->
io_lib:format("Unable to symlink directory ~s to ~s because \n~s~s",
[AppDir, TargetDir, rlx_util:indent(1),
- file:format_error(Reason)]).
+ file:format_error(Reason)]);
+format_error({tar_unknown_generation_error, Module, Vsn}) ->
+ io_lib:format("Tarball generation error of ~s ~s",
+ [Module, Vsn]);
+format_error({tar_generation_warn, Module, Warnings}) ->
+ io_lib:format("Tarball generation warnings for ~p : ~p",
+ [Module, Warnings]);
+format_error({tar_generation_error, Module, Errors}) ->
+ io_lib:format("Tarball generation error for ~p reason ~p",
+ [Module, Errors]).
%%%===================================================================
%%% Internal Functions
@@ -208,7 +245,7 @@ copy_dir(AppDir, TargetDir, SubDir) ->
end.
create_release_info(State0, Release0, OutputDir) ->
- RelName = erlang:atom_to_list(rlx_release:name(Release0)),
+ RelName = atom_to_list(rlx_release:name(Release0)),
ReleaseDir = release_output_dir(State0, Release0),
ReleaseFile = filename:join([ReleaseDir, RelName ++ ".rel"]),
ok = ec_file:mkdir_p(ReleaseDir),
@@ -330,9 +367,13 @@ include_erts(State, Release, OutputDir, RelDir) ->
ok = ec_file:copy(filename:join([Prefix, "bin", "start_clean.boot"]),
filename:join([OutputDir, "bin", "start_clean.boot"])),
NodeToolFile = nodetool_contents(),
+ InstallUpgradeFile = install_upgrade_escript_contents(),
NodeTool = filename:join([LocalErts, "bin", "nodetool"]),
+ InstallUpgrade = filename:join([LocalErts, "bin", "install_upgrade.escript"]),
ok = file:write_file(NodeTool, NodeToolFile),
- ok = file:change_mode(NodeTool, 8#755);
+ ok = file:write_file(InstallUpgrade, InstallUpgradeFile),
+ ok = file:change_mode(NodeTool, 8#755),
+ ok = file:change_mode(InstallUpgrade, 8#755);
false ->
ok
end,
@@ -358,13 +399,13 @@ make_boot_script(State, Release, OutputDir, RelDir) ->
ok ->
rlx_log:error(rlx_state:log(State),
"release successfully created!"),
- make_relup(State, Release);
+ {ok, State};
error ->
?RLX_ERROR({release_script_generation_error, ReleaseFile});
{ok, _, []} ->
rlx_log:error(rlx_state:log(State),
"release successfully created!"),
- make_relup(State, Release);
+ {ok, State};
{ok,Module,Warnings} ->
?RLX_ERROR({release_script_generation_warn, Module, Warnings});
{error,Module,Error} ->
@@ -385,30 +426,72 @@ make_script(Options, RunFun) ->
end.
make_relup(State, Release) ->
- case rlx_state:action(State) of
- relup ->
- UpFrom =
- case rlx_state:upfrom(State) of
- undefined ->
- get_last_release(State, Release);
- Vsn ->
- get_up_release(State, Release, Vsn)
- end,
- case UpFrom of
- undefined ->
- ?RLX_ERROR(no_upfrom_release_found);
- _ ->
- make_upfrom_script(State, Release, UpFrom)
- end;
+ UpFrom =
+ case rlx_state:upfrom(State) of
+ undefined ->
+ get_last_release(State, Release);
+ Vsn ->
+ get_up_release(State, Release, Vsn)
+ end,
+ case UpFrom of
+ undefined ->
+ ?RLX_ERROR(no_upfrom_release_found);
_ ->
- {ok, State}
+ make_upfrom_script(State, Release, UpFrom)
+ end.
+
+make_tar(State, Release, OutputDir) ->
+ Name = atom_to_list(rlx_release:name(Release)),
+ Vsn = rlx_release:vsn(Release),
+ Prefix = code:root_dir(),
+ ErtsVersion = rlx_release:erts(Release),
+ ErtsDir = filename:join([Prefix]),
+ case systools:make_tar(filename:join([OutputDir, "releases", Vsn, Name]),
+ [{path, [filename:join([OutputDir, "lib", "*", "ebin"])]},
+ {erts, ErtsDir},
+ {outdir, OutputDir}]) of
+ ok ->
+ TempDir = filename:join(OutputDir, integer_to_list(erlang:phash2(make_ref()))),
+ try
+ update_tar(State, TempDir, OutputDir, Name, Vsn, ErtsVersion)
+ catch
+ E:R ->
+ file:del_dir(TempDir),
+ ?RLX_ERROR({tar_generation_error, E, R})
+ end;
+ {ok, Module, Warnings} ->
+ ?RLX_ERROR({tar_generation_warn, Module, Warnings});
+ error ->
+ ?RLX_ERROR({tar_unknown_generation_error, Name, Vsn});
+ {error, Module, Errors} ->
+ ?RLX_ERROR({tar_generation_error, Module, Errors})
end.
+update_tar(State, TempDir, OutputDir, Name, Vsn, ErtsVersion) ->
+ TarFile = filename:join(OutputDir, Name++"-"++Vsn++".tar.gz"),
+ file:rename(filename:join(OutputDir, Name++".tar.gz"), TarFile),
+ erl_tar:extract(TarFile, [{cwd, TempDir}, compressed]),
+ ok = erl_tar:create(TarFile,
+ [{"erts-"++ErtsVersion, filename:join(TempDir, "erts-"++ErtsVersion)},
+ {filename:join(["erts-"++ErtsVersion, "bin", "nodetool"]),
+ hd(nodetool_contents())},
+ {filename:join(["erts-"++ErtsVersion, "bin", "install_upgrade.escript"]),
+ hd(install_upgrade_escript_contents())},
+ {"lib", filename:join(TempDir, "lib")},
+ {"releases", filename:join(TempDir, "releases")},
+ {filename:join(["releases", Vsn, "vm.args"]),
+ filename:join([OutputDir, "releases", Vsn, "vm.args"])},
+ {"bin", filename:join([OutputDir, "bin"])}], [compressed]),
+ rlx_log:info(rlx_state:log(State),
+ "tarball ~s successfully created!~n", [TarFile]),
+ rlx_util:delete_dir(TempDir),
+ {ok, State}.
+
make_upfrom_script(State, Release, UpFrom) ->
OutputDir = rlx_state:output_dir(State),
Options = [{outdir, OutputDir},
{path, get_code_paths(Release, OutputDir) ++
- get_code_paths(UpFrom, OutputDir)},
+ get_code_paths(UpFrom, OutputDir)},
silent],
CurrentRel = strip_rel(rlx_release:relfile(Release)),
UpFromRel = strip_rel(rlx_release:relfile(UpFrom)),
@@ -439,8 +522,7 @@ make_upfrom_script(State, Release, UpFrom) ->
write_relup_file(State, Release, Relup) ->
OutDir = release_output_dir(State, Release),
- RelName = rlx_util:to_string(rlx_release:name(Release)),
- RelupFile = filename:join(OutDir, RelName ++ ".relup"),
+ RelupFile = filename:join(OutDir, "relup"),
ok = ec_file:write_term(RelupFile, Relup).
strip_rel(Name) ->
@@ -485,7 +567,7 @@ release_output_dir(State, Release) ->
OutputDir = rlx_state:output_dir(State),
filename:join([OutputDir,
"releases",
- rlx_release:canonical_name(Release)]).
+ rlx_release:vsn(Release)]).
%% @doc Generates the correct set of code paths for the system.
-spec get_code_paths(rlx_release:t(), file:name()) -> [file:name()].
@@ -522,7 +604,7 @@ RELEASE_ROOT_DIR=`cd $SCRIPT_DIR/.. && pwd`
REL_NAME=">>, RelName, <<"
REL_VSN=">>, RelVsn, <<"
ERTS_VSN=">>, ErtsVsn, <<"
-REL_DIR=$RELEASE_ROOT_DIR/releases/$REL_NAME-$REL_VSN
+REL_DIR=$RELEASE_ROOT_DIR/releases/$REL_VSN
ERL_OPTS=">>, ErlOpts, <<"
find_erts_dir() {
@@ -568,9 +650,9 @@ RELEASE_ROOT_DIR=`cd $SCRIPT_DIR/.. && pwd`
REL_NAME=">>, RelName, <<"
REL_VSN=">>, RelVsn, <<"
ERTS_VSN=">>, ErtsVsn, <<"
-REL_DIR=$RELEASE_ROOT_DIR/releases/$REL_NAME-$REL_VSN
+REL_DIR=$RELEASE_ROOT_DIR/releases/$REL_VSN
ERL_OPTS=">>, ErlOpts, <<"
-PIPE_DIR=/tmp/$REL_DIR/
+PIPE_DIR=/tmp/erl_pipes/">>, RelName, <<"/
find_erts_dir() {
local erts_dir=$RELEASE_ROOT_DIR/erts-$ERTS_VSN
@@ -789,7 +871,7 @@ case \"$1\" in
node_name=`echo $NAME_ARG | awk '{print $2}'`
erlang_cookie=`echo $COOKIE_ARG | awk '{print $2}'`
- $ERTS_DIR/bin/escript $SCRIPT_DIR/bin/install_upgrade.escript $node_name $erlang_cookie $2
+ $ERTS_DIR/bin/escript $ERTS_DIR/bin/install_upgrade.escript $node_name $erlang_cookie $2
;;
console|console_clean|console_boot)
@@ -797,7 +879,7 @@ case \"$1\" in
# however, for debugging, sometimes start_clean.boot is useful.
# For e.g. 'setup', one may even want to name another boot script.
case \"$1\" in
- console) BOOTFILE=$REL_NAME ;;
+ console) [[ -f $REL_DIR/$REL_NAME ]] && BOOTFILE=$REL_NAME || BOOTFILE=start ;;
console_clean) BOOTFILE=start_clean ;;
console_boot)
shift
@@ -831,7 +913,7 @@ case \"$1\" in
# start up the release in the foreground for use by runit
# or other supervision services
- BOOTFILE=$REL_NAME
+ [[ -f $REL_DIR/$REL_NAME ]] && BOOTFILE=$REL_NAME || BOOTFILE=start
FOREGROUNDOPTIONS=\"-noinput +Bd\"
# Setup beam-required vars
@@ -860,6 +942,56 @@ esac
exit 0">>].
+install_upgrade_escript_contents() ->
+ [<<"#!/usr/bin/env escript
+%%! -noshell -noinput
+%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ft=erlang ts=4 sw=4 et
+
+-define(TIMEOUT, 60000).
+-define(INFO(Fmt,Args), io:format(Fmt,Args)).
+
+main([NodeName, Cookie, ReleasePackage]) ->TargetNode = start_distribution(NodeName, Cookie),
+ {ok, Cwd} = file:get_cwd(),
+ ok = rpc:call(TargetNode, file, set_cwd,
+ [Cwd], ?TIMEOUT),
+ case rpc:call(TargetNode, release_handler, unpack_release,
+ [ReleasePackage], ?TIMEOUT) of
+ {ok, Vsn} ->
+ ?INFO(\"Unpacked Release ~p~n\", [Vsn]),
+ {ok, OtherVsn, Desc} = rpc:call(TargetNode, release_handler,
+ check_install_release, [Vsn], ?TIMEOUT),
+ {ok, OtherVsn, Desc} = rpc:call(TargetNode, release_handler,
+ install_release, [Vsn], ?TIMEOUT),
+ ?INFO(\"Installed Release ~p~n\", [Vsn]),
+ ok = rpc:call(TargetNode, release_handler, make_permanent, [Vsn], ?TIMEOUT),
+ ?INFO(\"Made Release ~p Permanent~n\", [Vsn]);
+ {error, {existing_release, Vsn}} ->
+ ?INFO(\"Release ~s already installed~n\", [Vsn])
+ end;
+main(_) ->
+ init:stop(1).
+
+start_distribution(NodeName, Cookie) ->
+ MyNode = make_script_node(NodeName),
+ {ok, _Pid} = net_kernel:start([MyNode, longnames]),
+ erlang:set_cookie(node(), list_to_atom(Cookie)),
+ TargetNode = list_to_atom(NodeName),
+ case {net_kernel:connect_node(TargetNode),
+ net_adm:ping(TargetNode)} of
+ {true, pong} ->
+ ok;
+ {_, pang} ->
+ io:format(\"Node ~p not responding to pings.\n\", [TargetNode]),
+ init:stop(1)
+ end,
+ TargetNode.
+
+make_script_node(Node) ->
+ [Name, Host] = string:tokens(Node, \"@\"),
+ list_to_atom(lists:concat([Name, \"_upgrader_\", os:getpid(), \"@\", Host])).
+">>].
+
nodetool_contents() ->
[<<"%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
%% ex: ft=erlang ts=4 sw=4 et
diff --git a/src/rlx_prv_release.erl b/src/rlx_prv_release.erl
index ce14f4e..cf13953 100644
--- a/src/rlx_prv_release.erl
+++ b/src/rlx_prv_release.erl
@@ -142,7 +142,13 @@ solve_release(State0, DepGraph, RelName, RelVsn) ->
"Solving Release ~p-~s~n",
[RelName, RelVsn]),
try
- Release = rlx_state:get_configured_release(State0, RelName, RelVsn),
+ Release =
+ case get_realized_release(State0, RelName, RelVsn) of
+ undefined ->
+ rlx_state:get_configured_release(State0, RelName, RelVsn);
+ {ok, Release0} ->
+ rlx_release:relfile(rlx_state:get_configured_release(State0, RelName, RelVsn), rlx_release:relfile(Release0))
+ end,
Goals = rlx_release:goals(Release),
case Goals of
[] ->
@@ -176,6 +182,15 @@ set_resolved(State, Release0, Pkgs) ->
?RLX_ERROR({release_error, E})
end.
+get_realized_release(State, RelName, RelVsn) ->
+ try
+ Release = rlx_state:get_realized_release(State, RelName, RelVsn),
+ {ok, Release}
+ catch
+ throw:not_found ->
+ undefined
+ end.
+
%%%===================================================================
%%% Test Functions
%%%===================================================================
diff --git a/src/rlx_state.erl b/src/rlx_state.erl
index f5f363e..d03631d 100644
--- a/src/rlx_state.erl
+++ b/src/rlx_state.erl
@@ -25,7 +25,7 @@
-export([new/2,
log/1,
- action/1,
+ actions/1,
output_dir/1,
lib_dirs/1,
overrides/1,
@@ -72,18 +72,18 @@
-record(state_t, {log :: rlx_log:t(),
root_dir :: file:name(),
caller :: caller(),
- action :: atom(),
+ actions=[] :: [atom()],
output_dir :: file:name(),
lib_dirs=[] :: [file:name()],
config_file=[] :: file:filename() | undefined,
goals=[] :: [rlx_depsolver:constraint()],
- providers = [] :: [rlx_provider:t()],
- available_apps = [] :: [rlx_app_info:t()],
+ providers=[] :: [rlx_provider:t()],
+ available_apps=[] :: [rlx_app_info:t()],
default_configured_release :: {rlx_release:name(), rlx_release:vsn()},
vm_args :: file:filename() | undefined,
sys_config :: file:filename() | undefined,
overrides :: [{AppName::atom(), Directory::file:filename()}],
- skip_apps = [] :: [AppName::atom()],
+ skip_apps=[] :: [AppName::atom()],
configured_releases :: releases(),
realized_releases :: releases(),
upfrom :: string() | binary() | undefined,
@@ -106,17 +106,17 @@
%% API
%%============================================================================
%% @doc Create a new 'log level' for the system
--spec new(proplists:proplist(), atom()) -> t().
-new(PropList, Target)
+-spec new(proplists:proplist(), [atom()]) -> t().
+new(PropList, Targets)
when erlang:is_list(PropList),
- erlang:is_atom(Target) ->
+ erlang:is_list(Targets) ->
{ok, Root} = file:get_cwd(),
State0 =
#state_t{log = proplists:get_value(log, PropList, rlx_log:new(error)),
output_dir=proplists:get_value(output_dir, PropList, ""),
lib_dirs=[to_binary(Dir) || Dir <- proplists:get_value(lib_dirs, PropList, [])],
config_file=proplists:get_value(config, PropList, undefined),
- action = Target,
+ actions = Targets,
caller = proplists:get_value(caller, PropList, api),
goals=proplists:get_value(goals, PropList, []),
providers = [],
@@ -132,10 +132,10 @@ new(PropList, Target)
disable_default_libs,
proplists:get_value(disable_default_libs, PropList, false)).
-%% @doc the action targeted for this system
--spec action(t()) -> atom().
-action(#state_t{action=Action}) ->
- Action.
+%% @doc the actions targeted for this system
+-spec actions(t()) -> atom().
+actions(#state_t{actions=Actions}) ->
+ Actions.
%% @doc the application overrides for the system
-spec overrides(t()) -> [{AppName::atom(), Directory::file:filename()}].
@@ -359,7 +359,7 @@ to_binary(Dir)
new_test() ->
LogState = rlx_log:new(error),
- RCLState = new([{log, LogState}], release),
+ RCLState = new([{log, LogState}], [release]),
?assertMatch(LogState, log(RCLState)).
-endif.
diff --git a/src/rlx_util.erl b/src/rlx_util.erl
index ac6af5c..c821896 100644
--- a/src/rlx_util.erl
+++ b/src/rlx_util.erl
@@ -21,7 +21,8 @@
%%% @doc Trivial utility file to help handle common tasks
-module(rlx_util).
--export([mkdir_p/1,
+-export([delete_dir/1,
+ mkdir_p/1,
to_binary/1,
to_string/1,
to_atom/1,
@@ -39,6 +40,19 @@
%%============================================================================
%% API
%%============================================================================
+%% @doc Deletes non-empty directory
+delete_dir(Path) ->
+ lists:foldr(fun(File, ok) ->
+ case filelib:is_dir(File) of
+ true ->
+ file:del_dir(File);
+ false ->
+ file:delete(File)
+ end
+ end, ok, filelib:wildcard(filename:join(Path, "**"))),
+ ok = file:del_dir(Path).
+
+
%% @doc Makes a directory including parent dirs if they are missing.
-spec mkdir_p(string()) -> ok | {error, Reason::file:posix()}.
mkdir_p(Path) ->