1%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- 2%% ex: ts=4 sw=4 et 3 4-module(rebar_prv_path). 5 6-behaviour(provider). 7 8-export([init/1, 9 do/1, 10 format_error/1]). 11 12-include("rebar.hrl"). 13 14-define(PROVIDER, path). 15-define(DEPS, [app_discovery]). 16 17%% =================================================================== 18%% Public API 19%% =================================================================== 20 21-spec init(rebar_state:t()) -> {ok, rebar_state:t()}. 22init(State) -> 23 State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER}, 24 {module, ?MODULE}, 25 {bare, true}, 26 {deps, ?DEPS}, 27 {example, "rebar3 path"}, 28 {short_desc, "Print paths to build dirs in current profile."}, 29 {desc, "Print paths to build dirs in current profile."}, 30 {opts, path_opts(State)}])), 31 32 {ok, State1}. 33 34-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}. 35do(State) -> 36 {RawOpts, _} = rebar_state:command_parsed_args(State), 37 %% retrieve apps to filter by for other args 38 Apps = filter_apps(RawOpts, State), 39 %% remove apps and separator opts from options 40 Paths = lists:filter(fun({app, _}) -> false; ({separator, _}) -> false; (_) -> true end, RawOpts), 41 %% if no paths requested in opts print the base_dir instead 42 P = case Paths of [] -> [{ebin, true}]; _ -> Paths end, 43 paths(P, Apps, State, []), 44 {ok, State}. 45 46-spec format_error(any()) -> iolist(). 47format_error(Reason) -> 48 io_lib:format("~p", [Reason]). 49 50filter_apps(RawOpts, State) -> 51 RawApps = proplists:get_all_values(app, RawOpts), 52 Apps = lists:foldl(fun(String, Acc) -> rebar_string:lexemes(String, ",") ++ Acc end, [], RawApps), 53 case Apps of 54 [] -> 55 ProjectDeps = project_deps(State), 56 ProjectApps = rebar_state:project_apps(State), 57 lists:map(fun(A) -> binary_to_list(rebar_app_info:name(A)) end, ProjectApps) ++ ProjectDeps; 58 _ -> Apps 59 end. 60 61 62paths([], _, State, Acc) -> print_paths_if_exist(lists:reverse(Acc), State); 63paths([{base, true}|Rest], Apps, State, Acc) -> 64 paths(Rest, Apps, State, [base_dir(State)|Acc]); 65paths([{bin, true}|Rest], Apps, State, Acc) -> 66 paths(Rest, Apps, State, [bin_dir(State)|Acc]); 67paths([{ebin, true}|Rest], Apps, State, Acc) -> 68 paths(Rest, Apps, State, ebin_dirs(Apps, State) ++ Acc); 69paths([{lib, true}|Rest], Apps, State, Acc) -> 70 paths(Rest, Apps, State, [lib_dir(State)|Acc]); 71paths([{priv, true}|Rest], Apps, State, Acc) -> 72 paths(Rest, Apps, State, priv_dirs(Apps, State) ++ Acc); 73paths([{src, true}|Rest], Apps, State, Acc) -> 74 paths(Rest, Apps, State, src_dirs(Apps, State) ++ Acc); 75paths([{rel, true}|Rest], Apps, State, Acc) -> 76 paths(Rest, Apps, State, [rel_dir(State)|Acc]). 77 78base_dir(State) -> io_lib:format("~ts", [rebar_dir:base_dir(State)]). 79bin_dir(State) -> io_lib:format("~ts/bin", [rebar_dir:base_dir(State)]). 80lib_dir(State) -> io_lib:format("~ts", [rebar_dir:deps_dir(State)]). 81rel_dir(State) -> io_lib:format("~ts/rel", [rebar_dir:base_dir(State)]). 82 83ebin_dirs(Apps, State) -> 84 lists:flatmap(fun(App) -> [io_lib:format("~ts/~ts/ebin", [rebar_dir:checkouts_out_dir(State), App]), 85 io_lib:format("~ts/~ts/ebin", [rebar_dir:deps_dir(State), App]) ] end, Apps). 86priv_dirs(Apps, State) -> 87 lists:flatmap(fun(App) -> [io_lib:format("~ts/~ts/priv", [rebar_dir:checkouts_out_dir(State), App]), 88 io_lib:format("~ts/~ts/priv", [rebar_dir:deps_dir(State), App]) ] end, Apps). 89src_dirs(Apps, State) -> 90 lists:flatmap(fun(App) -> [io_lib:format("~ts/~ts/src", [rebar_dir:checkouts_out_dir(State), App]), 91 io_lib:format("~ts/~ts/src", [rebar_dir:deps_dir(State), App]) ] end, Apps). 92 93print_paths_if_exist(Paths, State) -> 94 {RawOpts, _} = rebar_state:command_parsed_args(State), 95 Sep = proplists:get_value(separator, RawOpts, " "), 96 RealPaths = lists:filter(fun(P) -> ec_file:is_dir(P) end, Paths), 97 io:format("~ts", [rebar_string:join(RealPaths, Sep)]). 98 99project_deps(State) -> 100 Profiles = rebar_state:current_profiles(State), 101 DepList = lists:foldl(fun(Profile, Acc) -> rebar_state:get(State, {deps, Profile}, []) ++ Acc end, [], Profiles), 102 LockList = lists:foldl(fun(Profile, Acc) -> rebar_state:get(State, {locks, Profile}, []) ++ Acc end, [], Profiles), 103 Deps = [normalize(name(Dep)) || Dep <- DepList++LockList], 104 lists:usort(Deps). 105 106name(App) when is_tuple(App) -> element(1, App); 107name(Name) when is_binary(Name); is_list(Name); is_atom(Name) -> Name. 108 109normalize(AppName) when is_list(AppName) -> AppName; 110normalize(AppName) when is_atom(AppName) -> atom_to_list(AppName); 111normalize(AppName) when is_binary(AppName) -> binary_to_list(AppName). 112 113path_opts(_State) -> 114 [{app, undefined, "app", string, help(app)}, 115 {base, undefined, "base", boolean, help(base)}, 116 {bin, undefined, "bin", boolean, help(bin)}, 117 {ebin, undefined, "ebin", boolean, help(ebin)}, 118 {lib, undefined, "lib", boolean, help(lib)}, 119 {priv, undefined, "priv", boolean, help(priv)}, 120 {separator, $s, "separator", string, help(separator)}, 121 {src, undefined, "src", boolean, help(src)}, 122 {rel, undefined, "rel", boolean, help(rel)}]. 123 124help(app) -> "Comma separated list of applications to return paths for."; 125help(base) -> "Return the `base' path of the current profile."; 126help(bin) -> "Return the `bin' path of the current profile."; 127help(ebin) -> "Return all `ebin' paths of the current profile's applications."; 128help(lib) -> "Return the `lib' path of the current profile."; 129help(priv) -> "Return the `priv' path of the current profile's applications."; 130help(separator) -> "In case of multiple return paths, the separator character to use to join them."; 131help(src) -> "Return the `src' path of the current profile's applications."; 132help(rel) -> "Return the `rel' path of the current profile.". 133