diff options
-rw-r--r-- | src/rebar_dir.erl | 29 | ||||
-rw-r--r-- | src/rebar_file_utils.erl | 29 | ||||
-rw-r--r-- | test/rebar_dir_SUITE.erl | 38 | ||||
-rw-r--r-- | test/rebar_file_utils_SUITE.erl | 24 |
4 files changed, 111 insertions, 9 deletions
diff --git a/src/rebar_dir.erl b/src/rebar_dir.erl index 7220d89..364e197 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:path_from_ancestor(rebar_file_utils:canonical_path(Path), rebar_state:dir(State)) of + {ok, NewPath} -> filename:join([base_dir(State), NewPath]); + %% not relative to project root, don't modify + {error, badparent} -> 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:path_from_ancestor(rebar_file_utils:canonical_path(Path), rebar_app_info:dir(App)) of + {ok, NewPath} -> filename:join([rebar_app_info:out_dir(App), NewPath]); + {error, badparent} -> retarget_path(State, Path, Rest) + end. diff --git a/src/rebar_file_utils.erl b/src/rebar_file_utils.erl index 732d83f..ea1a6a2 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, + path_from_ancestor/2, + canonical_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 path_from_ancestor(string(), string()) -> {ok, string()} | {error, badparent}. + +path_from_ancestor(Target, To) -> + path_from_ancestor_(filename:split(canonical_path(Target)), + filename:split(canonical_path(To))). + +path_from_ancestor_([Part|Target], [Part|To]) -> path_from_ancestor_(Target, To); +path_from_ancestor_([], []) -> {ok, ""}; +path_from_ancestor_(Target, []) -> {ok, filename:join(Target)}; +path_from_ancestor_(_, _) -> {error, badparent}. + + +%% reduce a filepath by removing all incidences of `.' and `..' +-spec canonical_path(string()) -> string(). + +canonical_path(Dir) -> canonical_path([], filename:split(filename:absname(Dir))). + +canonical_path([], []) -> filename:nativename("/"); +canonical_path(Acc, []) -> filename:join(lists:reverse(Acc)); +canonical_path(Acc, ["."|Rest]) -> canonical_path(Acc, Rest); +canonical_path([_|Acc], [".."|Rest]) -> canonical_path(Acc, Rest); +canonical_path([], [".."|Rest]) -> canonical_path([], Rest); +canonical_path(Acc, [Component|Rest]) -> canonical_path([Component|Acc], Rest). + %% =================================================================== %% Internal functions %% =================================================================== diff --git a/test/rebar_dir_SUITE.erl b/test/rebar_dir_SUITE.erl index 6fbc081..a9e44db 100644 --- a/test/rebar_dir_SUITE.erl +++ b/test/rebar_dir_SUITE.erl @@ -5,6 +5,7 @@ -export([default_src_dirs/1, default_extra_src_dirs/1, default_all_src_dirs/1]). -export([src_dirs/1, extra_src_dirs/1, all_src_dirs/1]). -export([profile_src_dirs/1, profile_extra_src_dirs/1, profile_all_src_dirs/1]). +-export([retarget_path/1]). -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). @@ -13,16 +14,22 @@ all() -> [default_src_dirs, default_extra_src_dirs, default_all_src_dirs, src_dirs, extra_src_dirs, all_src_dirs, - profile_src_dirs, profile_extra_src_dirs, profile_all_src_dirs]. + profile_src_dirs, profile_extra_src_dirs, profile_all_src_dirs, + retarget_path]. init_per_testcase(_, Config) -> C = rebar_test_utils:init_rebar_state(Config), AppDir = ?config(apps, C), - Name = rebar_test_utils:create_random_name("app1_"), - Vsn = rebar_test_utils:create_random_vsn(), - rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), - C. + Name1 = rebar_test_utils:create_random_name("app1_"), + Vsn1 = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(filename:join([AppDir,"apps",Name1]), Name1, Vsn1, [kernel, stdlib]), + + Name2 = rebar_test_utils:create_random_name("app2_"), + Vsn2 = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(filename:join([AppDir,"apps",Name2]), Name2, Vsn2, [kernel, stdlib]), + + [{app_one, Name1}, {app_two, Name2}] ++ C. end_per_testcase(_, _Config) -> ok. @@ -97,3 +104,24 @@ profile_all_src_dirs(Config) -> R = lists:sort(["foo", "bar", "baz", "qux"]), R = lists:sort(rebar_dir:all_src_dirs(rebar_state:opts(State))). + +retarget_path(Config) -> + {ok, State} = rebar_test_utils:run_and_check(Config, [], ["compile"], return), + + BaseDir = rebar_dir:base_dir(State), + + Name1 = ?config(app_one, Config), + Name2 = ?config(app_two, Config), + + ?assertEqual(filename:join([BaseDir, "lib", Name1, "test"]), + rebar_dir:retarget_path(State, filename:join([rebar_dir:root_dir(State), "apps", Name1, "test"]))), + ?assertEqual(filename:join([BaseDir, "lib", Name2, "test"]), + rebar_dir:retarget_path(State, filename:join([rebar_dir:root_dir(State), "apps", Name2, "test"]))), + ?assertEqual(filename:join([BaseDir, "lib", Name1, "more_test"]), + rebar_dir:retarget_path(State, filename:join([rebar_dir:root_dir(State), "apps", Name1, "more_test"]))), + ?assertEqual(filename:join([BaseDir, "test"]), + rebar_dir:retarget_path(State, filename:join([rebar_dir:root_dir(State), "test"]))), + ?assertEqual(filename:join([BaseDir, "some_other_dir"]), + rebar_dir:retarget_path(State, filename:join([rebar_dir:root_dir(State), "some_other_dir"]))), + ?assertEqual("/somewhere/outside/the/project", + rebar_dir:retarget_path(State, "/somewhere/outside/the/project")).
\ No newline at end of file diff --git a/test/rebar_file_utils_SUITE.erl b/test/rebar_file_utils_SUITE.erl index 03a2d54..0f444d5 100644 --- a/test/rebar_file_utils_SUITE.erl +++ b/test/rebar_file_utils_SUITE.erl @@ -10,7 +10,9 @@ multi_tmpdir/1, reset_nonexistent_dir/1, reset_empty_dir/1, - reset_dir/1]). + reset_dir/1, + path_from_ancestor/1, + canonical_path/1]). -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). @@ -19,7 +21,8 @@ all() -> [{group, tmpdir}, - {group, reset_dir}]. + {group, reset_dir}, + path_from_ancestor, canonical_path]. groups() -> [{tmpdir, [], [raw_tmpdir, empty_tmpdir, simple_tmpdir, multi_tmpdir]}, @@ -84,3 +87,20 @@ reset_dir(Config) -> ok = rebar_file_utils:reset_dir(TmpDir), ?assert(filelib:is_dir(TmpDir)), {ok, []} = file:list_dir(TmpDir). + +path_from_ancestor(_Config) -> + ?assertEqual({ok, "foo/bar/baz"}, rebar_file_utils:path_from_ancestor("/foo/bar/baz", "/")), + ?assertEqual({ok, "bar/baz"}, rebar_file_utils:path_from_ancestor("/foo/bar/baz", "/foo")), + ?assertEqual({ok, "bar"}, rebar_file_utils:path_from_ancestor("foo/bar", "foo")), + ?assertEqual({ok, "bar"}, rebar_file_utils:path_from_ancestor("foo/bar/", "foo/")), + ?assertEqual({error, badparent}, rebar_file_utils:path_from_ancestor("/foo/bar/baz", "/qux")), + ?assertEqual({error, badparent}, rebar_file_utils:path_from_ancestor("/foo/bar/baz", "/foo/bar/baz/qux")). + +canonical_path(_Config) -> + ?assertEqual("/", rebar_file_utils:canonical_path("/")), + ?assertEqual("/", rebar_file_utils:canonical_path("/../../..")), + ?assertEqual("/foo", rebar_file_utils:canonical_path("/foo/bar/..")), + ?assertEqual("/foo", rebar_file_utils:canonical_path("/foo/../foo")), + ?assertEqual("/foo", rebar_file_utils:canonical_path("/foo/.")), + ?assertEqual("/foo", rebar_file_utils:canonical_path("/foo/./.")), + ?assertEqual("/foo/bar", rebar_file_utils:canonical_path("/foo/./bar")).
\ No newline at end of file |