Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

133 строки
5.7 KiB

10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
  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 = all_apps_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).
  113. %% The union of all entries in the applications list for an app and
  114. %% the deps listed in its rebar.config is all deps that may be needed
  115. %% for building the app.
  116. all_apps_deps(App) ->
  117. Applications = lists:usort([atom_to_binary(X, utf8) || X <- rebar_app_info:applications(App)]),
  118. Deps = lists:usort(lists:map(fun({Name, _}) -> Name; (Name) -> Name end, rebar_app_info:deps(App))),
  119. lists:umerge(Deps, Applications).