summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTING.md6
-rw-r--r--THANKS1
-rw-r--r--src/rebar.hrl1
-rw-r--r--src/rebar_base_compiler.erl24
-rw-r--r--src/rebar_dialyzer_format.erl16
-rw-r--r--src/rebar_dir.erl39
-rw-r--r--src/rebar_file_utils.erl117
-rw-r--r--src/rebar_prv_dialyzer.erl7
-rw-r--r--test/rebar_file_utils_SUITE.erl190
9 files changed, 344 insertions, 57 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 8077813..16de1e5 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -162,7 +162,7 @@ $ ./rebar3 ct
```
Most tests are named according to their module name followed by the `_SUITE`
-suffi. Providers are made shorter, such that `rebar_prv_new` is tested in
+suffix. Providers are made shorter, such that `rebar_prv_new` is tested in
`rebar_new_SUITE`.
Most tests in the test suite will rely on calling Rebar3 in its API form,
@@ -285,7 +285,7 @@ The reviewer may ask you to later squash the commits together to provide
a clean commit history before merging in the feature.
It's important to write a proper commit title and description. The commit title
-should fir around 50 characters; it is the first line of the commit text. The
+should be no more than 50 characters; it is the first line of the commit text. The
second line of the commit text must be left blank. The third line and beyond is
the commit message. You should write a commit message. If you do, wrap all
lines at 72 characters. You should explain what the commit does, what
@@ -299,7 +299,7 @@ maintainers. When opening a pull request, explain what the patch is doing
and if it makes sense, why the proposed implementation was chosen.
Try to use well-defined commits (one feature per commit) so that reading
-them and testing them is easier for reviewers and while bissecting the code
+them and testing them is easier for reviewers and while bisecting the code
base for issues.
During the review process, you may be asked to correct or edit a few things
diff --git a/THANKS b/THANKS
index 63f4aaa..e91d941 100644
--- a/THANKS
+++ b/THANKS
@@ -138,3 +138,4 @@ Carlos Eduardo de Paula
Derek Brown
Heinz N. Gies
Roberto Aloi
+Andrew McRobb
diff --git a/src/rebar.hrl b/src/rebar.hrl
index c94a84a..ca44283 100644
--- a/src/rebar.hrl
+++ b/src/rebar.hrl
@@ -27,6 +27,7 @@
-define(REMOTE_PACKAGE_DIR, "tarballs").
-define(REMOTE_REGISTRY_FILE, "registry.ets.gz").
-define(LOCK_FILE, "rebar.lock").
+-define(DEFAULT_COMPILER_SOURCE_FORMAT, relative).
-define(PACKAGE_INDEX_VERSION, 3).
-define(PACKAGE_TABLE, package_index).
diff --git a/src/rebar_base_compiler.erl b/src/rebar_base_compiler.erl
index dcb1975..480e49c 100644
--- a/src/rebar_base_compiler.erl
+++ b/src/rebar_base_compiler.erl
@@ -35,7 +35,6 @@
error_tuple/4,
format_error_source/2]).
--define(DEFAULT_COMPILER_SOURCE_FORMAT, relative).
-type desc() :: term().
-type loc() :: {line(), col()} | line().
-type line() :: integer().
@@ -138,28 +137,7 @@ error_tuple(Source, Es, Ws, Opts) ->
-spec format_error_source(file:filename(), rebar_dict() | [{_,_}]) ->
file:filename().
format_error_source(Path, Opts) ->
- Type = case rebar_opts:get(Opts, compiler_source_format,
- ?DEFAULT_COMPILER_SOURCE_FORMAT) of
- V when V == absolute; V == relative; V == build ->
- V;
- Other ->
- ?WARN("Invalid argument ~p for compiler_source_format - "
- "assuming ~s~n", [Other, ?DEFAULT_COMPILER_SOURCE_FORMAT]),
- ?DEFAULT_COMPILER_SOURCE_FORMAT
- end,
- case Type of
- absolute -> resolve_linked_source(Path);
- build -> Path;
- relative ->
- Cwd = rebar_dir:get_cwd(),
- rebar_dir:make_relative_path(resolve_linked_source(Path), Cwd)
- end.
-
-%% @private takes a filename and canonicalizes its path if it is a link.
--spec resolve_linked_source(file:filename()) -> file:filename().
-resolve_linked_source(Src) ->
- {Dir, Base} = rebar_file_utils:split_dirname(Src),
- filename:join(rebar_file_utils:resolve_link(Dir), Base).
+ rebar_dir:format_source_file_name(Path, Opts).
%% ===================================================================
%% Internal functions
diff --git a/src/rebar_dialyzer_format.erl b/src/rebar_dialyzer_format.erl
index b30c4dc..1d234c3 100644
--- a/src/rebar_dialyzer_format.erl
+++ b/src/rebar_dialyzer_format.erl
@@ -16,18 +16,19 @@
-include("rebar.hrl").
--export([format_warnings/1]).
+-export([format_warnings/2]).
%% Formats a list of warnings in a nice per file way. Note that we reverse
%% the list at the end to 'undo' the reversal by foldl
-format_warnings(Warnings) ->
- {_, Res} = lists:foldl(fun format_warning_/2, {undefined, []}, Warnings),
+format_warnings(Opts, Warnings) ->
+ Fold = fun(Warning, Acc) -> format_warning_(Opts, Warning, Acc) end,
+ {_, Res} = lists:foldl(Fold, {undefined, []}, Warnings),
lists:reverse(Res).
%% If the last seen file is and the file of this warning are the same
%% we skip the file header
-format_warning_(Warning = {_Tag, {File, Line}, Msg}, {File, Acc}) ->
+format_warning_(_Opts, Warning = {_Tag, {File, Line}, Msg}, {File, Acc}) ->
try
String = message_to_string(Msg),
{File, [lists:flatten(fmt("~!c~4w~!!: ~s", [Line, String])) | Acc]}
@@ -39,8 +40,9 @@ format_warning_(Warning = {_Tag, {File, Line}, Msg}, {File, Acc}) ->
end;
%% With a new file detencted we also write a file header.
-format_warning_(Warning = {_Tag, {File, Line}, Msg}, {_LastFile, Acc}) ->
+format_warning_(Opts, Warning = {_Tag, {SrcFile, Line}, Msg}, {_LastFile, Acc}) ->
try
+ File = rebar_dir:format_source_file_name(SrcFile, Opts),
Base = filename:basename(File),
Dir = filename:dirname(File),
Root = filename:rootname(Base),
@@ -49,12 +51,12 @@ format_warning_(Warning = {_Tag, {File, Line}, Msg}, {_LastFile, Acc}) ->
Base1 = fmt("~!_c~s~!!~!__~s", [Root, Ext]),
F = fmt("~!__~s", [filename:join(Path, Base1)]),
String = message_to_string(Msg),
- {File, [lists:flatten(fmt("~n~s~n~!c~4w~!!: ~s", [F, Line, String])) | Acc]}
+ {SrcFile, [lists:flatten(fmt("~n~s~n~!c~4w~!!: ~s", [F, Line, String])) | Acc]}
catch
Error:Reason ->
?DEBUG("Failed to pretty format warning: ~p:~p~n~p",
[Error, Reason, erlang:get_stacktrace()]),
- {File, [dialyzer:format_warning(Warning, fullpath) | Acc]}
+ {SrcFile, [dialyzer:format_warning(Warning, fullpath) | Acc]}
end.
fmt(Fmt) ->
diff --git a/src/rebar_dir.erl b/src/rebar_dir.erl
index 069d8fd..b61bfcc 100644
--- a/src/rebar_dir.erl
+++ b/src/rebar_dir.erl
@@ -26,7 +26,8 @@
src_dir_opts/2, recursive/2,
extra_src_dirs/1, extra_src_dirs/2,
all_src_dirs/1, all_src_dirs/3,
- retarget_path/2]).
+ retarget_path/2,
+ format_source_file_name/2]).
-include("rebar.hrl").
@@ -334,3 +335,39 @@ retarget_path(State, Path, [App|Rest]) ->
{ok, NewPath} -> filename:join([rebar_app_info:out_dir(App), NewPath]);
{error, badparent} -> retarget_path(State, Path, Rest)
end.
+
+format_source_file_name(Path, Opts) ->
+ Type = case rebar_opts:get(Opts, compiler_source_format,
+ ?DEFAULT_COMPILER_SOURCE_FORMAT) of
+ V when V == absolute; V == relative; V == build ->
+ V;
+ Other ->
+ warn_source_format_once(Other)
+ end,
+ case Type of
+ absolute -> resolve_linked_source(Path);
+ build -> Path;
+ relative ->
+ Cwd = rebar_dir:get_cwd(),
+ rebar_dir:make_relative_path(resolve_linked_source(Path), Cwd)
+ end.
+
+%% @private displays a warning for the compiler source format option
+%% only once
+-spec warn_source_format_once(term()) -> ok.
+warn_source_format_once(Format) ->
+ Warn = application:get_env(rebar, warn_source_format) =/= {ok, false},
+ application:set_env(rebar, warn_source_format, false),
+ case Warn of
+ false ->
+ ok;
+ true ->
+ ?WARN("Invalid argument ~p for compiler_source_format - "
+ "assuming ~s~n", [Format, ?DEFAULT_COMPILER_SOURCE_FORMAT])
+ end.
+
+%% @private takes a filename and canonicalizes its path if it is a link.
+-spec resolve_linked_source(file:filename()) -> file:filename().
+resolve_linked_source(Src) ->
+ {Dir, Base} = rebar_file_utils:split_dirname(Src),
+ filename:join(rebar_file_utils:resolve_link(Dir), Base).
diff --git a/src/rebar_file_utils.erl b/src/rebar_file_utils.erl
index 8645641..8158312 100644
--- a/src/rebar_file_utils.erl
+++ b/src/rebar_file_utils.erl
@@ -185,32 +185,113 @@ mv(Source, Dest) ->
ok
end;
{win32, _} ->
- Cmd = case filelib:is_dir(Source) of
- true ->
- ?FMT("robocopy /move /e \"~s\" \"~s\" 1> nul",
- [rebar_utils:escape_double_quotes(filename:nativename(Source)),
- rebar_utils:escape_double_quotes(filename:nativename(Dest))]);
- false ->
- ?FMT("robocopy /move /e \"~s\" \"~s\" \"~s\" 1> nul",
- [rebar_utils:escape_double_quotes(filename:nativename(filename:dirname(Source))),
- rebar_utils:escape_double_quotes(filename:nativename(Dest)),
- rebar_utils:escape_double_quotes(filename:basename(Source))])
- end,
- Res = rebar_utils:sh(Cmd,
- [{use_stdout, false}, return_on_error]),
- case win32_ok(Res) of
- true -> ok;
+ case filelib:is_dir(Source) of
+ true ->
+ SrcDir = filename:nativename(Source),
+ DestDir = case filelib:is_dir(Dest) of
+ true ->
+ %% to simulate unix/posix mv, we have to replicate
+ %% the same directory movement by moving the whole
+ %% top-level directory, not just the insides
+ SrcName = filename:basename(Source),
+ filename:nativename(filename:join(Dest, SrcName));
+ false ->
+ filename:nativename(Dest)
+ end,
+ robocopy_dir(SrcDir, DestDir);
false ->
- {error, lists:flatten(
- io_lib:format("Failed to move ~s to ~s~n",
- [Source, Dest]))}
+ SrcDir = filename:nativename(filename:dirname(Source)),
+ SrcName = filename:basename(Source),
+ DestDir = filename:nativename(filename:dirname(Dest)),
+ DestName = filename:basename(Dest),
+ IsDestDir = filelib:is_dir(Dest),
+ if IsDestDir ->
+ %% if basename and target name are different because
+ %% we move to a directory, then just move there.
+ %% Similarly, if they are the same but we're going to
+ %% a directory, let's just do that directly.
+ FullDestDir = filename:nativename(Dest),
+ robocopy_file(SrcDir, FullDestDir, SrcName)
+ ; SrcName =:= DestName ->
+ %% if basename and target name are the same and both are files,
+ %% we do a regular move with robocopy without rename.
+ robocopy_file(SrcDir, DestDir, DestName)
+ ; SrcName =/= DestName->
+ robocopy_mv_and_rename(Source, Dest, SrcDir, SrcName, DestDir, DestName)
+ end
+
end
end.
+robocopy_mv_and_rename(Source, Dest, SrcDir, SrcName, DestDir, DestName) ->
+ %% If we're moving a file and the origin and
+ %% destination names are different:
+ %% - mktmp
+ %% - robocopy source_dir tmp_dir srcname
+ %% - rename srcname destname (to avoid clobbering)
+ %% - robocopy tmp_dir dest_dir destname
+ %% - remove tmp_dir
+ case ec_file:insecure_mkdtemp() of
+ {error, _Reason} ->
+ {error, lists:flatten(
+ io_lib:format("Failed to move ~s to ~s (tmpdir failed)~n",
+ [Source, Dest]))};
+ TmpPath ->
+ case robocopy_file(SrcDir, TmpPath, SrcName) of
+ {error, Reason} ->
+ {error, Reason};
+ ok ->
+ TmpSrc = filename:join(TmpPath, SrcName),
+ TmpDst = filename:join(TmpPath, DestName),
+ case file:rename(TmpSrc, TmpDst) of
+ {error, _} ->
+ {error, lists:flatten(
+ io_lib:format("Failed to move ~s to ~s (via rename)~n",
+ [Source, Dest]))};
+ ok ->
+ case robocopy_file(TmpPath, DestDir, DestName) of
+ Err = {error, _} -> Err;
+ OK -> rm_rf(TmpPath), OK
+ end
+ end
+ end
+ end.
+
+robocopy_file(SrcPath, DestPath, FileName) ->
+ Cmd = ?FMT("robocopy /move /e \"~s\" \"~s\" \"~s\"",
+ [rebar_utils:escape_double_quotes(SrcPath),
+ rebar_utils:escape_double_quotes(DestPath),
+ rebar_utils:escape_double_quotes(FileName)]),
+ Res = rebar_utils:sh(Cmd, [{use_stdout, false}, return_on_error]),
+ case win32_ok(Res) of
+ false ->
+ {error, lists:flatten(
+ io_lib:format("Failed to move ~s to ~s~n",
+ [filename:join(SrcPath, FileName),
+ filename:join(DestPath, FileName)]))};
+ true ->
+ ok
+ end.
+
+robocopy_dir(Source, Dest) ->
+ Cmd = ?FMT("robocopy /move /e \"~s\" \"~s\"",
+ [rebar_utils:escape_double_quotes(Source),
+ rebar_utils:escape_double_quotes(Dest)]),
+ Res = rebar_utils:sh(Cmd,
+ [{use_stdout, false}, return_on_error]),
+ case win32_ok(Res) of
+ true -> ok;
+ false ->
+ {error, lists:flatten(
+ io_lib:format("Failed to move ~s to ~s~n",
+ [Source, Dest]))}
+ end.
+
win32_ok({ok, _}) -> true;
win32_ok({error, {Rc, _}}) when Rc<9; Rc=:=16 -> true;
win32_ok(_) -> false.
+
delete_each([]) ->
ok;
delete_each([File | Rest]) ->
diff --git a/src/rebar_prv_dialyzer.erl b/src/rebar_prv_dialyzer.erl
index 44dc0d2..21d7f5a 100644
--- a/src/rebar_prv_dialyzer.erl
+++ b/src/rebar_prv_dialyzer.erl
@@ -478,7 +478,8 @@ run_dialyzer(State, Opts, Output) ->
{check_plt, false} |
Opts],
?DEBUG("Running dialyzer with options: ~p~n", [Opts2]),
- Warnings = format_warnings(Output, dialyzer:run(Opts2)),
+ Warnings = format_warnings(rebar_state:opts(State),
+ Output, dialyzer:run(Opts2)),
{Warnings, State};
false ->
Opts2 = [{warnings, no_warnings()},
@@ -497,8 +498,8 @@ legacy_warnings(Warnings) ->
Warnings
end.
-format_warnings(Output, Warnings) ->
- Warnings1 = rebar_dialyzer_format:format_warnings(Warnings),
+format_warnings(Opts, Output, Warnings) ->
+ Warnings1 = rebar_dialyzer_format:format_warnings(Opts, Warnings),
console_warnings(Warnings1),
file_warnings(Output, Warnings),
length(Warnings).
diff --git a/test/rebar_file_utils_SUITE.erl b/test/rebar_file_utils_SUITE.erl
index 7285e13..4cc6a93 100644
--- a/test/rebar_file_utils_SUITE.erl
+++ b/test/rebar_file_utils_SUITE.erl
@@ -4,6 +4,8 @@
groups/0,
init_per_group/2,
end_per_group/2,
+ init_per_testcase/2,
+ end_per_testcase/2,
raw_tmpdir/1,
empty_tmpdir/1,
simple_tmpdir/1,
@@ -15,7 +17,13 @@
canonical_path/1,
resolve_link/1,
split_dirname/1,
- mv_warning_is_ignored/1]).
+ mv_warning_is_ignored/1,
+ mv_dir/1,
+ mv_file_same/1,
+ mv_file_diff/1,
+ mv_file_dir_same/1,
+ mv_file_dir_diff/1,
+ mv_no_clobber/1]).
-include_lib("common_test/include/ct.hrl").
-include_lib("eunit/include/eunit.hrl").
@@ -25,6 +33,7 @@
all() ->
[{group, tmpdir},
{group, reset_dir},
+ {group, mv},
path_from_ancestor,
canonical_path,
resolve_link,
@@ -33,7 +42,9 @@ all() ->
groups() ->
[{tmpdir, [], [raw_tmpdir, empty_tmpdir, simple_tmpdir, multi_tmpdir]},
- {reset_dir, [], [reset_nonexistent_dir, reset_empty_dir, reset_dir]}].
+ {reset_dir, [], [reset_nonexistent_dir, reset_empty_dir, reset_dir]},
+ {mv, [], [mv_dir, mv_file_same, mv_file_diff,
+ mv_file_dir_same, mv_file_dir_diff, mv_no_clobber]}].
init_per_group(reset_dir, Config) ->
TmpDir = rebar_file_utils:system_tmpdir(["rebar_file_utils_SUITE", "resetable"]),
@@ -41,6 +52,20 @@ init_per_group(reset_dir, Config) ->
init_per_group(_, Config) -> Config.
end_per_group(_, Config) -> Config.
+init_per_testcase(Test, Config) ->
+ case os:type() of
+ {win32, _} ->
+ case lists:member(Test, [resolve_link, mv_warning_is_ignored]) of
+ true -> {skip, "broken in windows"};
+ false -> Config
+ end;
+ _ ->
+ Config
+ end.
+
+end_per_testcase(_Test, Config) ->
+ Config.
+
raw_tmpdir(_Config) ->
case rebar_file_utils:system_tmpdir() of
"/tmp" -> ok;
@@ -143,3 +168,164 @@ mv_warning_is_ignored(_Config) ->
meck:expect(rebar_utils, sh, fun("mv ding dong", _) -> {ok, "Warning"} end),
ok = rebar_file_utils:mv("ding", "dong"),
meck:unload(rebar_utils).
+
+%%% Ensure Windows & Unix operations to move files
+
+mv_dir(Config) ->
+ %% Move a directory to another one location
+ PrivDir = ?config(priv_dir, Config),
+ BaseDir = mk_base_dir(PrivDir, mv_dir),
+ SrcDir = filename:join(BaseDir, "src/"),
+ ec_file:mkdir_p(SrcDir),
+ ?assert(filelib:is_dir(SrcDir)),
+ %% empty dir movement
+ DstDir1 = filename:join(BaseDir, "dst1/"),
+ ?assertNot(filelib:is_dir(DstDir1)),
+ ?assertEqual(ok, rebar_file_utils:mv(SrcDir, DstDir1)),
+ ?assert(filelib:is_dir(DstDir1)),
+
+ %% move files from dir to empty dir
+ F1 = filename:join(SrcDir, "file1"),
+ F2 = filename:join(SrcDir, "subdir/file2"),
+ filelib:ensure_dir(F2),
+ file:write_file(F1, "hello"),
+ file:write_file(F2, "world"),
+ DstDir2 = filename:join(BaseDir, "dst2/"),
+ D2F1 = filename:join(DstDir2, "file1"),
+ D2F2 = filename:join(DstDir2, "subdir/file2"),
+ ?assertNot(filelib:is_dir(DstDir2)),
+ ?assertEqual(ok, rebar_file_utils:mv(SrcDir, DstDir2)),
+ ?assert(filelib:is_file(D2F1)),
+ ?assert(filelib:is_file(D2F2)),
+
+ %% move files from dir to existing dir moves it to
+ %% a subdir
+ filelib:ensure_dir(F2),
+ file:write_file(F1, "hello"),
+ file:write_file(F2, "world"),
+ DstDir3 = filename:join(BaseDir, "dst3/"),
+ D3F1 = filename:join(DstDir3, "src/file1"),
+ D3F2 = filename:join(DstDir3, "src/subdir/file2"),
+ ec_file:mkdir_p(DstDir3),
+ ?assert(filelib:is_dir(DstDir3)),
+ ?assertEqual(ok, rebar_file_utils:mv(SrcDir, DstDir3)),
+ ?assertNot(filelib:is_file(F1)),
+ ?assertNot(filelib:is_file(F2)),
+ ?assert(filelib:is_file(D3F1)),
+ ?assert(filelib:is_file(D3F2)),
+ ?assertNot(filelib:is_dir(SrcDir)),
+ ok.
+
+mv_file_same(Config) ->
+ %% Move a file from a directory to the other without renaming
+ PrivDir = ?config(priv_dir, Config),
+ BaseDir = mk_base_dir(PrivDir, mv_file_same),
+ SrcDir = filename:join(BaseDir, "src/"),
+ ec_file:mkdir_p(SrcDir),
+ ?assert(filelib:is_dir(SrcDir)),
+ F = filename:join(SrcDir, "file"),
+ file:write_file(F, "hello"),
+ DstDir = filename:join(BaseDir, "dst/"),
+ ec_file:mkdir_p(DstDir),
+ Dst = filename:join(DstDir, "file"),
+ ?assertEqual(ok, rebar_file_utils:mv(F, Dst)),
+ ?assert(filelib:is_file(Dst)),
+ ?assertNot(filelib:is_file(F)),
+ ok.
+
+mv_file_diff(Config) ->
+ %% Move a file from a directory to another one while renaming
+ %% into a pre-existing file
+ PrivDir = ?config(priv_dir, Config),
+ BaseDir = mk_base_dir(PrivDir, mv_file_diff),
+ SrcDir = filename:join(BaseDir, "src/"),
+ ec_file:mkdir_p(SrcDir),
+ ?assert(filelib:is_dir(SrcDir)),
+ F = filename:join(SrcDir, "file"),
+ file:write_file(F, "hello"),
+ DstDir = filename:join(BaseDir, "dst/"),
+ ec_file:mkdir_p(DstDir),
+ Dst = filename:join(DstDir, "file-rename"),
+ file:write_file(Dst, "not-the-right-content"),
+ ?assert(filelib:is_file(Dst)),
+ ?assertEqual(ok, rebar_file_utils:mv(F, Dst)),
+ ?assert(filelib:is_file(Dst)),
+ ?assertEqual({ok, <<"hello">>}, file:read_file(Dst)),
+ ?assertNot(filelib:is_file(F)),
+ ok.
+
+mv_file_dir_same(Config) ->
+ %% Move a file to a directory without renaming
+ PrivDir = ?config(priv_dir, Config),
+ BaseDir = mk_base_dir(PrivDir, mv_file_dir_same),
+ SrcDir = filename:join(BaseDir, "src/"),
+ ec_file:mkdir_p(SrcDir),
+ ?assert(filelib:is_dir(SrcDir)),
+ F = filename:join(SrcDir, "file"),
+ file:write_file(F, "hello"),
+ DstDir = filename:join(BaseDir, "dst/"),
+ ec_file:mkdir_p(DstDir),
+ Dst = filename:join(DstDir, "file"),
+ ?assert(filelib:is_dir(DstDir)),
+ ?assertEqual(ok, rebar_file_utils:mv(F, DstDir)),
+ ?assert(filelib:is_file(Dst)),
+ ?assertNot(filelib:is_file(F)),
+ ok.
+
+mv_file_dir_diff(Config) ->
+ %% Move a file to a directory while renaming
+ PrivDir = ?config(priv_dir, Config),
+ BaseDir = mk_base_dir(PrivDir, mv_file_dir_diff),
+ SrcDir = filename:join(BaseDir, "src/"),
+ ec_file:mkdir_p(SrcDir),
+ ?assert(filelib:is_dir(SrcDir)),
+ F = filename:join(SrcDir, "file"),
+ file:write_file(F, "hello"),
+ DstDir = filename:join(BaseDir, "dst/"),
+ ec_file:mkdir_p(DstDir),
+ Dst = filename:join(DstDir, "file-rename"),
+ ?assert(filelib:is_dir(DstDir)),
+ ?assertNot(filelib:is_file(Dst)),
+ ?assertEqual(ok, rebar_file_utils:mv(F, Dst)),
+ ?assert(filelib:is_file(Dst)),
+ ?assertNot(filelib:is_file(F)),
+ ok.
+
+mv_no_clobber(Config) ->
+ %% Moving a file while renaming does not clobber other files
+ PrivDir = ?config(priv_dir, Config),
+ BaseDir = mk_base_dir(PrivDir, mv_no_clobber),
+ SrcDir = filename:join(BaseDir, "src/"),
+ ec_file:mkdir_p(SrcDir),
+ ?assert(filelib:is_dir(SrcDir)),
+ F = filename:join(SrcDir, "file"),
+ file:write_file(F, "hello"),
+ FBad = filename:join(SrcDir, "file-alt"),
+ file:write_file(FBad, "wrong-data"),
+ DstDir = filename:join(BaseDir, "dst/"),
+ ec_file:mkdir_p(DstDir),
+ Dst = filename:join(DstDir, "file-alt"),
+ DstBad = filename:join(DstDir, "file"),
+ file:write_file(DstBad, "wrong-data"),
+ ?assert(filelib:is_file(F)),
+ ?assert(filelib:is_file(FBad)),
+ ?assert(filelib:is_dir(DstDir)),
+ ?assertNot(filelib:is_file(Dst)),
+ ?assert(filelib:is_file(DstBad)),
+ ?assertEqual(ok, rebar_file_utils:mv(F, Dst)),
+ ?assert(filelib:is_file(Dst)),
+ ?assertNot(filelib:is_file(F)),
+ ?assert(filelib:is_file(DstBad)),
+ ?assert(filelib:is_file(FBad)),
+ ?assertEqual({ok, <<"hello">>}, file:read_file(Dst)),
+ ?assertEqual({ok, <<"wrong-data">>}, file:read_file(FBad)),
+ ?assertEqual({ok, <<"wrong-data">>}, file:read_file(DstBad)),
+ ok.
+
+
+mk_base_dir(BasePath, Name) ->
+ {_,_,Micro} = os:timestamp(),
+ Index = integer_to_list(Micro),
+ Path = filename:join(BasePath, atom_to_list(Name) ++ Index),
+ ec_file:mkdir_p(Path),
+ Path.