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
|
-module(rebar_digraph).
-export([sort_apps/1
,restore_graph/1
,solve/2
,subgraph/2
,format_error/1]).
-include("rebar.hrl").
sort_apps(Apps) ->
Graph = digraph:new(),
lists:foreach(fun(App) ->
Name = rebar_app_info:name(App),
Deps = rebar_app_info:deps(App),
add(Graph, {Name, Deps})
end, Apps),
case digraph_utils:topsort(Graph) of
false ->
{error, no_sort};
V ->
{ok, names_to_apps(lists:reverse(V), Apps)}
end.
add(Graph, {PkgName, Deps}) ->
case digraph:vertex(Graph, PkgName) of
false ->
V = digraph:add_vertex(Graph, PkgName);
{V, []} ->
V
end,
lists:foreach(fun(DepName) ->
V3 = case digraph:vertex(Graph, DepName) of
false ->
digraph:add_vertex(Graph, DepName);
{V2, []} ->
V2
end,
digraph:add_edge(Graph, V, V3)
end, Deps).
restore_graph({Vs, Es}) ->
Graph = digraph:new(),
lists:foreach(fun({V, LastUpdated}) ->
digraph:add_vertex(Graph, V, LastUpdated)
end, Vs),
lists:foreach(fun({V1, V2}) ->
digraph:add_edge(Graph, V1, V2)
end, Es),
Graph.
solve(Graph, Vertices) ->
solve(Graph, Vertices, lists:foldl(fun({Key, _}=N, Solution) ->
dict:store(Key, N, Solution)
end, dict:new(), Vertices)).
solve(_Graph, [], Solution) ->
{_, Vertices} = lists:unzip(dict:to_list(Solution)),
{ok, Vertices};
solve(Graph, Vertices, Solution) ->
{NV, NS} =
lists:foldl(fun(V, {NewVertices, SolutionAcc}) ->
OutNeighbors = digraph:out_neighbours(Graph, V),
lists:foldl(fun({Key, _}=N, {NewVertices1, SolutionAcc1}) ->
case dict:is_key(Key, SolutionAcc1) of
true ->
{NewVertices1, SolutionAcc1};
false ->
{[N | NewVertices1], dict:store(Key, N, SolutionAcc1)}
end
end, {NewVertices, SolutionAcc}, OutNeighbors)
end, {[], Solution}, Vertices),
solve(Graph, NV, NS).
subgraph(Graph, Vertices) ->
digraph_utils:subgraph(Graph, Vertices).
format_error(no_solution) ->
io_lib:format("No solution for packages found.", []).
%%====================================================================
%% Internal Functions
%%====================================================================
-spec names_to_apps([atom()], [rebar_app_info:t()]) -> [rebar_app_info:t()].
names_to_apps(Names, Apps) ->
[element(2, App) || App <- [find_app_by_name(Name, Apps) || Name <- Names], App =/= error].
-spec find_app_by_name(atom(), [rebar_app_info:t()]) -> {ok, rebar_app_info:t()} | error.
find_app_by_name(Name, Apps) ->
ec_lists:find(fun(App) ->
ec_cnv:to_atom(rebar_app_info:name(App)) =:= ec_cnv:to_atom(Name)
end, Apps).
|