summaryrefslogtreecommitdiff
path: root/src/rebar_prv_deps.erl
blob: a88b0147d68af51e92b8efcc63506e4323e6aad5 (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
-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, []}])),
    {ok, State1}.

-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
    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}.

-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) ->
    atom_to_binary(Name, unicode);
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("~ts* (package ~ts)", [rebar_utils:to_binary(Name), rebar_utils:to_binary(Vsn)]);
display_dep(_State, Name) when is_binary(Name) ->
    ?CONSOLE("~ts* (package)", [Name]);
display_dep(_State, {Name, Source}) when is_tuple(Source) ->
    ?CONSOLE("~ts* (~ts source)", [rebar_utils:to_binary(Name), type(Source)]);
display_dep(_State, {Name, _Vsn, Source}) when is_tuple(Source) ->
    ?CONSOLE("~ts* (~ts source)", [rebar_utils:to_binary(Name), type(Source)]);
display_dep(_State, {Name, _Vsn, Source, _Opts}) when is_tuple(Source) ->
    ?CONSOLE("~ts* (~ts source)", [rebar_utils: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, rebar_utils:to_binary(Name)]),
    NeedsUpdate = case rebar_fetch:needs_update(AppDir, Source, State) of
        true -> "*";
        false -> ""
    end,
    ?CONSOLE("~ts~ts (locked package ~ts)", [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, rebar_utils:to_binary(Name)]),
    NeedsUpdate = case rebar_fetch:needs_update(AppDir, Source, State) of
        true -> "*";
        false -> ""
    end,
    ?CONSOLE("~ts~ts (locked ~ts source)", [Name, NeedsUpdate, type(Source)]).

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