Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.

116 righe
4.6 KiB

10 anni fa
10 anni fa
10 anni fa
  1. -module(rebar_digraph).
  2. -export([compile_order/1
  3. ,restore_graph/1
  4. ,cull_deps/2
  5. ,subgraph/2
  6. ,format_error/1]).
  7. -include("rebar.hrl").
  8. %% Sort apps with topological sort to get proper build order
  9. compile_order(Apps) ->
  10. Graph = digraph:new(),
  11. lists:foreach(fun(App) ->
  12. Name = rebar_app_info:name(App),
  13. Deps = rebar_app_info:deps(App),
  14. add(Graph, {Name, Deps})
  15. end, Apps),
  16. case digraph_utils:topsort(Graph) of
  17. false ->
  18. case digraph_utils:is_acyclic(Graph) of
  19. true ->
  20. {error, no_sort};
  21. false ->
  22. Cycles = lists:sort(
  23. [lists:sort(Comp) || Comp <- digraph_utils:strong_components(Graph),
  24. length(Comp)>1]),
  25. {error, {cycles, Cycles}}
  26. end;
  27. V ->
  28. {ok, names_to_apps(lists:reverse(V), Apps)}
  29. end.
  30. add(Graph, {PkgName, Deps}) ->
  31. case digraph:vertex(Graph, PkgName) of
  32. false ->
  33. V = digraph:add_vertex(Graph, PkgName);
  34. {V, []} ->
  35. V
  36. end,
  37. lists:foreach(fun(DepName) ->
  38. case DepName of
  39. {Name, _Vsn} ->
  40. Name;
  41. Name ->
  42. Name
  43. end,
  44. V3 = case digraph:vertex(Graph, Name) of
  45. false ->
  46. digraph:add_vertex(Graph, Name);
  47. {V2, []} ->
  48. V2
  49. end,
  50. digraph:add_edge(Graph, V, V3)
  51. end, Deps).
  52. restore_graph({Vs, Es}) ->
  53. Graph = digraph:new(),
  54. lists:foreach(fun({V, LastUpdated}) ->
  55. digraph:add_vertex(Graph, V, LastUpdated)
  56. end, Vs),
  57. lists:foreach(fun({V1, V2}) ->
  58. digraph:add_edge(Graph, V1, V2)
  59. end, Es),
  60. Graph.
  61. %% Pick packages to fullfill dependencies
  62. %% The first dep while traversing the graph is chosen and any conflicting
  63. %% dep encountered later on is ignored.
  64. cull_deps(Graph, Vertices) ->
  65. cull_deps(Graph,
  66. Vertices,
  67. lists:foldl(fun({Key, _}=N, Solution) -> dict:store(Key, N, Solution) end,
  68. dict:new(), Vertices),
  69. []).
  70. cull_deps(_Graph, [], Solution, Discarded) ->
  71. {_, Vertices} = lists:unzip(dict:to_list(Solution)),
  72. {ok, Vertices, Discarded};
  73. cull_deps(Graph, Vertices, Solution, Discarded) ->
  74. {NV, NS, DS} =
  75. lists:foldl(fun(V, {NewVertices, SolutionAcc, DiscardedAcc}) ->
  76. OutNeighbors = digraph:out_neighbours(Graph, V),
  77. lists:foldl(fun({Key, _}=N, {NewVertices1, SolutionAcc1, DiscardedAcc1}) ->
  78. case dict:find(Key, SolutionAcc1) of
  79. {ok, N} -> % already seen
  80. {NewVertices1, SolutionAcc1, DiscardedAcc1};
  81. {ok, _} -> % conflict resolution!
  82. {NewVertices1, SolutionAcc1, [N|DiscardedAcc1]};
  83. error ->
  84. {[N | NewVertices1], dict:store(Key, N, SolutionAcc1), DiscardedAcc1}
  85. end
  86. end, {NewVertices, SolutionAcc, DiscardedAcc}, OutNeighbors)
  87. end, {[], Solution, Discarded}, lists:sort(Vertices)),
  88. cull_deps(Graph, NV, NS, DS).
  89. subgraph(Graph, Vertices) ->
  90. digraph_utils:subgraph(Graph, Vertices).
  91. format_error(no_solution) ->
  92. io_lib:format("No solution for packages found.", []).
  93. %%====================================================================
  94. %% Internal Functions
  95. %%====================================================================
  96. -spec names_to_apps([atom()], [rebar_app_info:t()]) -> [rebar_app_info:t()].
  97. names_to_apps(Names, Apps) ->
  98. [element(2, App) || App <- [find_app_by_name(Name, Apps) || Name <- Names], App =/= error].
  99. -spec find_app_by_name(atom(), [rebar_app_info:t()]) -> {ok, rebar_app_info:t()} | error.
  100. find_app_by_name(Name, Apps) ->
  101. ec_lists:find(fun(App) ->
  102. binary_to_atom(rebar_app_info:name(App), utf8) =:= binary_to_atom(Name, utf8)
  103. end, Apps).