summaryrefslogtreecommitdiff
path: root/src/rebar_digraph.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/rebar_digraph.erl')
-rw-r--r--src/rebar_digraph.erl98
1 files changed, 98 insertions, 0 deletions
diff --git a/src/rebar_digraph.erl b/src/rebar_digraph.erl
new file mode 100644
index 0000000..27bbdd2
--- /dev/null
+++ b/src/rebar_digraph.erl
@@ -0,0 +1,98 @@
+-module(rebar_digraph).
+
+-export([compile_order/1
+ ,restore_graph/1
+ ,cull_deps/2
+ ,subgraph/2
+ ,format_error/1]).
+
+-include("rebar.hrl").
+
+%% Sort apps with topological sort to get proper build order
+compile_order(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.
+
+%% Pick packages to fullfill dependencies
+%% The first dep while traversing the graph is chosen and any conflicting
+%% dep encountered later on is ignored.
+cull_deps(Graph, Vertices) ->
+ cull_deps(Graph, Vertices, lists:foldl(fun({Key, _}=N, Solution) ->
+ dict:store(Key, N, Solution)
+ end, dict:new(), Vertices)).
+
+cull_deps(_Graph, [], Solution) ->
+ {_, Vertices} = lists:unzip(dict:to_list(Solution)),
+ {ok, Vertices};
+cull_deps(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),
+ cull_deps(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).