summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/rebar_dir.erl29
-rw-r--r--src/rebar_file_utils.erl29
2 files changed, 56 insertions, 2 deletions
diff --git a/src/rebar_dir.erl b/src/rebar_dir.erl
index 7220d89..bbc7394 100644
--- a/src/rebar_dir.erl
+++ b/src/rebar_dir.erl
@@ -23,7 +23,8 @@
make_relative_path/2,
src_dirs/1, src_dirs/2,
extra_src_dirs/1, extra_src_dirs/2,
- all_src_dirs/1, all_src_dirs/3]).
+ all_src_dirs/1, all_src_dirs/3,
+ retarget_path/2]).
-include("rebar.hrl").
@@ -160,3 +161,29 @@ all_src_dirs(Opts) -> all_src_dirs(Opts, [], []).
list(file:filename_all()).
all_src_dirs(Opts, SrcDefault, ExtraDefault) ->
src_dirs(Opts, SrcDefault) ++ extra_src_dirs(Opts, ExtraDefault).
+
+%% given a path if that path is an ancestor of an app dir return the path relative to that
+%% apps outdir. if the path is not an ancestor to any app dirs but is an ancestor of the
+%% project root return the path relative to the project base_dir. if it is not an ancestor
+%% of either return it unmodified
+-spec retarget_path(rebar_state:t(), string()) -> string().
+
+retarget_path(State, Path) ->
+ ProjectApps = rebar_state:project_apps(State),
+ retarget_path(State, Path, ProjectApps).
+
+%% not relative to any apps in project, check to see it's relative to
+%% project root
+retarget_path(State, Path, []) ->
+ case rebar_file_utils:relative_path(rebar_file_utils:reduce_path(Path), rebar_state:dir(State)) of
+ {ok, NewPath} -> filename:join([base_dir(State), NewPath]);
+ %% not relative to project root, don't modify
+ {error, not_relative} -> Path
+ end;
+%% relative to current app, retarget to the same dir relative to
+%% the app's out_dir
+retarget_path(State, Path, [App|Rest]) ->
+ case rebar_file_utils:relative_path(rebar_file_utils:reduce_path(Path), rebar_app_info:dir(App)) of
+ {ok, NewPath} -> filename:join([rebar_app_info:out_dir(App), NewPath]);
+ {error, not_relative} -> retarget_path(State, Path, Rest)
+ end.
diff --git a/src/rebar_file_utils.erl b/src/rebar_file_utils.erl
index 732d83f..26d4da0 100644
--- a/src/rebar_file_utils.erl
+++ b/src/rebar_file_utils.erl
@@ -37,7 +37,9 @@
system_tmpdir/0,
system_tmpdir/1,
reset_dir/1,
- touch/1]).
+ touch/1,
+ relative_path/2,
+ reduce_path/1]).
-include("rebar.hrl").
@@ -244,6 +246,31 @@ touch(Path) ->
ok = file:write_file_info(Path, A#file_info{mtime = calendar:local_time(),
atime = calendar:local_time()}).
+%% for a given path return the path relative to a base directory
+-spec relative_path(string(), string()) -> {ok, string()} | {error, not_relative}.
+
+relative_path(Target, To) ->
+ relative_path1(filename:split(reduce_path(Target)),
+ filename:split(reduce_path(To))).
+
+relative_path1([Part|Target], [Part|To]) -> relative_path1(Target, To);
+relative_path1([], []) -> {ok, ""};
+relative_path1(Target, []) -> {ok, filename:join(Target)};
+relative_path1(_, _) -> {error, not_relative}.
+
+
+%% reduce a filepath by removing all incidences of `.' and `..'
+-spec reduce_path(string()) -> string().
+
+reduce_path(Dir) -> reduce_path([], filename:split(filename:absname(Dir))).
+
+reduce_path([], []) -> filename:nativename("/");
+reduce_path(Acc, []) -> filename:join(lists:reverse(Acc));
+reduce_path(Acc, ["."|Rest]) -> reduce_path(Acc, Rest);
+reduce_path([_|Acc], [".."|Rest]) -> reduce_path(Acc, Rest);
+reduce_path([], [".."|Rest]) -> reduce_path([], Rest);
+reduce_path(Acc, [Component|Rest]) -> reduce_path([Component|Acc], Rest).
+
%% ===================================================================
%% Internal functions
%% ===================================================================