summaryrefslogtreecommitdiff
path: root/src/rebar_prv_deps.erl
blob: dc356a840ecd323af28a218304a5cd758a1436ca (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
-module(rebar_prv_deps).

-behaviour(provider).

-export([init/1,
         do/1,
         format_error/1]).

-include("rebar.hrl").

-define(PROVIDER, deps).
-define(DEPS, [app_discovery]).

-spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
init(State) ->
    State1 = rebar_state:add_provider(
            State,
            providers:create([
                    {name, ?PROVIDER},
                    {module, ?MODULE},
                    {bare, true},
                    {deps, ?DEPS},
                    {example, "rebar3 deps"},
                    {short_desc, "List dependencies"},
                    {desc, "List dependencies. Those not matching lock files "
                           "are followed by an asterisk (*)."},
                    {opts, [{tree, $t, "tree", undefined, "Display package dependencies in tree format (git and hg deps not supported)."}]}])),
    {ok, State1}.

-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
    case display_tree(State) of
        true ->
            {_Packages, Graph} = rebar_state:packages(State),
            List = merge_deps_per_profile(State),
            {_SrcDeps, PkgDeps} = rebar_prv_install_deps:parse_deps("", List, State, [], 0),
            rebar_digraph:print_solution(Graph, PkgDeps),
            {ok, State};
        false ->
            Profiles = rebar_state:current_profiles(State),
            List = [{Profile, rebar_state:get(State, {deps, Profile}, [])}
                   || Profile <- Profiles],
            [display(State, Profile, Deps) || {Profile, Deps} <- List],
            {ok, State}
    end.

-spec format_error(any()) -> iolist().
format_error(Reason) ->
    io_lib:format("~p", [Reason]).

display(State, default, Deps) ->
    NewDeps = merge(Deps, rebar_state:get(State, deps, [])),
    display_deps(State, NewDeps),
    ?CONSOLE("", []);
display(State, Profile, Deps) ->
    ?CONSOLE("-- ~p --", [Profile]),
    display_deps(State, Deps),
    ?CONSOLE("", []).

merge(Deps, SourceDeps) ->
    merge1(dedup([normalize(Dep) || Dep <- Deps]),
           [normalize(Dep) || Dep <- SourceDeps]).

normalize(Name) when is_binary(Name) ->
    Name;
normalize(Name) when is_atom(Name) ->
    ec_cnv:to_binary(Name);
normalize(Dep) when is_tuple(Dep) ->
    Name = element(1, Dep),
    setelement(1, Dep, normalize(Name)).

merge1(Deps, SourceDeps) ->
    Names = [name(Dep) || Dep <- Deps],
    ToAdd = [Dep || Dep <- SourceDeps,
                    not lists:member(name(Dep), Names)],
    Deps ++ ToAdd.

%% Keep the latter one as locks come after regular deps in the list.
%% This is totally not safe as an assumption, but it's what we got.
%% We do this by comparing the current element and looking if a
%% similar named one happens later. If so, drop the current one.
dedup(Deps) -> dedup(Deps, [name(Dep) || Dep <- Deps]).

dedup([], []) -> [];
dedup([Dep|Deps], [Name|DepNames]) ->
    case lists:member(Name, DepNames) of
        true -> dedup(Deps, DepNames);
        false -> [Dep | dedup(Deps, DepNames)]
    end.

name(T) when is_tuple(T) -> element(1, T);
name(B) when is_binary(B) -> B.


display_deps(State, Deps) ->
    lists:foreach(fun(Dep) -> display_dep(State, Dep) end, Deps).

%% packages
display_dep(_State, {Name, Vsn}) when is_list(Vsn) ->
    ?CONSOLE("~s* (package ~s)", [ec_cnv:to_binary(Name), ec_cnv:to_binary(Vsn)]);
display_dep(_State, Name) when is_binary(Name) ->
    ?CONSOLE("~s* (package)", [Name]);
display_dep(_State, {Name, Source}) when is_tuple(Source) ->
    ?CONSOLE("~s* (~s source)", [ec_cnv:to_binary(Name), type(Source)]);
display_dep(_State, {Name, _Vsn, Source}) when is_tuple(Source) ->
    ?CONSOLE("~s* (~s source)", [ec_cnv:to_binary(Name), type(Source)]);
display_dep(_State, {Name, _Vsn, Source, _Opts}) when is_tuple(Source) ->
    ?CONSOLE("~s* (~s source)", [ec_cnv:to_binary(Name), type(Source)]);
%% Locked
display_dep(State, {Name, Source={pkg, _, Vsn}, Level}) when is_integer(Level) ->
    DepsDir = rebar_dir:deps_dir(State),
    AppDir = filename:join([DepsDir, ec_cnv:to_binary(Name)]),
    NeedsUpdate = case rebar_fetch:needs_update(AppDir, Source, State) of
        true -> "*";
        false -> ""
    end,
    ?CONSOLE("~s~s (locked package ~s)", [Name, NeedsUpdate, Vsn]);
display_dep(State, {Name, Source, Level}) when is_tuple(Source), is_integer(Level) ->
    DepsDir = rebar_dir:deps_dir(State),
    AppDir = filename:join([DepsDir, ec_cnv:to_binary(Name)]),
    NeedsUpdate = case rebar_fetch:needs_update(AppDir, Source, State) of
        true -> "*";
        false -> ""
    end,
    ?CONSOLE("~s~s (locked ~s source)", [Name, NeedsUpdate, type(Source)]).

type(Source) when is_tuple(Source) -> element(1, Source).

display_tree(State) ->
    {Args, _} = rebar_state:command_parsed_args(State),
    proplists:get_value(tree, Args, false).

merge_deps_per_profile(State) ->
    Profiles = rebar_state:current_profiles(State),
    lists:foldl(fun(Profile, Deps) ->
                        D = rebar_utils:deps_to_binary(rebar_state:get(State, {deps, Profile}, [])),
                        D1 = rebar_utils:tup_sort(D),
                        rebar_utils:tup_dedup(
                          rebar_utils:tup_umerge(D1
                                                ,Deps))
                end, [], Profiles).