25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

125 lines
5.3 KiB

  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. Name1 = case DepName of
  39. {Name, _Vsn} ->
  40. ec_cnv:to_binary(Name);
  41. Name ->
  42. ec_cnv:to_binary(Name)
  43. end,
  44. V3 = case digraph:vertex(Graph, Name1) of
  45. false ->
  46. digraph:add_vertex(Graph, Name1);
  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. 1,
  68. lists:foldl(fun({Key, _}, Levels) ->
  69. dict:store(Key, 0, Levels)
  70. end, dict:new(), Vertices),
  71. lists:foldl(fun({Key, _}=N, Solution) ->
  72. dict:store(Key, N, Solution)
  73. end, dict:new(), Vertices),
  74. []).
  75. cull_deps(_Graph, [], _Level, Levels, Solution, Discarded) ->
  76. {_, Vertices} = lists:unzip(dict:to_list(Solution)),
  77. LvlVertices = [{App,Vsn,dict:fetch(App,Levels)} || {App,Vsn} <- Vertices],
  78. {ok, LvlVertices, Discarded};
  79. cull_deps(Graph, Vertices, Level, Levels, Solution, Discarded) ->
  80. {NV, NS, LS, DS} =
  81. lists:foldl(fun(V, {NewVertices, SolutionAcc, LevelsAcc, DiscardedAcc}) ->
  82. OutNeighbors = lists:keysort(1, digraph:out_neighbours(Graph, V)),
  83. lists:foldl(fun({Key, _}=N, {NewVertices1, SolutionAcc1, LevelsAcc1, DiscardedAcc1}) ->
  84. case dict:find(Key, SolutionAcc1) of
  85. {ok, N} -> % already seen
  86. {NewVertices1, SolutionAcc1, LevelsAcc, DiscardedAcc1};
  87. {ok, _} -> % conflict resolution!
  88. {NewVertices1, SolutionAcc1, LevelsAcc, [N|DiscardedAcc1]};
  89. error ->
  90. {[N | NewVertices1],
  91. dict:store(Key, N, SolutionAcc1),
  92. dict:store(Key, Level, LevelsAcc1),
  93. DiscardedAcc1}
  94. end
  95. end, {NewVertices, SolutionAcc, LevelsAcc, DiscardedAcc}, OutNeighbors)
  96. end, {[], Solution, Levels, Discarded}, lists:keysort(1, Vertices)),
  97. cull_deps(Graph, NV, Level+1, LS, NS, DS).
  98. subgraph(Graph, Vertices) ->
  99. digraph_utils:subgraph(Graph, Vertices).
  100. format_error(no_solution) ->
  101. io_lib:format("No solution for packages found.", []).
  102. %%====================================================================
  103. %% Internal Functions
  104. %%====================================================================
  105. -spec names_to_apps([atom()], [rebar_app_info:t()]) -> [rebar_app_info:t()].
  106. names_to_apps(Names, Apps) ->
  107. [element(2, App) || App <- [find_app_by_name(Name, Apps) || Name <- Names], App =/= error].
  108. -spec find_app_by_name(atom(), [rebar_app_info:t()]) -> {ok, rebar_app_info:t()} | error.
  109. find_app_by_name(Name, Apps) ->
  110. ec_lists:find(fun(App) ->
  111. rebar_app_info:name(App) =:= Name
  112. end, Apps).