You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

96 lines
3.5 KiB

  1. -module(rebar_digraph).
  2. -export([compile_order/1
  3. ,restore_graph/1
  4. ,subgraph/2
  5. ,format_error/1]).
  6. -include("rebar.hrl").
  7. %% Sort apps with topological sort to get proper build order
  8. compile_order(Apps) ->
  9. Graph = digraph:new(),
  10. lists:foreach(fun(App) ->
  11. Name = rebar_app_info:name(App),
  12. Deps = all_apps_deps(App),
  13. add(Graph, {Name, Deps})
  14. end, Apps),
  15. Order =
  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. true = digraph:delete(Graph),
  31. Order.
  32. add(Graph, {PkgName, Deps}) ->
  33. case digraph:vertex(Graph, PkgName) of
  34. false ->
  35. V = digraph:add_vertex(Graph, PkgName);
  36. {V, []} ->
  37. V
  38. end,
  39. lists:foreach(fun(DepName) ->
  40. Name1 = case DepName of
  41. {Name, _Vsn} ->
  42. ec_cnv:to_binary(Name);
  43. Name ->
  44. ec_cnv:to_binary(Name)
  45. end,
  46. V3 = case digraph:vertex(Graph, Name1) of
  47. false ->
  48. digraph:add_vertex(Graph, Name1);
  49. {V2, []} ->
  50. V2
  51. end,
  52. digraph:add_edge(Graph, V, V3)
  53. end, Deps).
  54. restore_graph({Vs, Es}) ->
  55. Graph = digraph:new(),
  56. lists:foreach(fun({V, LastUpdated}) ->
  57. digraph:add_vertex(Graph, V, LastUpdated)
  58. end, Vs),
  59. lists:foreach(fun({V1, V2}) ->
  60. digraph:add_edge(Graph, V1, V2)
  61. end, Es),
  62. Graph.
  63. format_error(no_solution) ->
  64. io_lib:format("No solution for packages found.", []).
  65. %%====================================================================
  66. %% Internal Functions
  67. %%====================================================================
  68. subgraph(Graph, Vertices) ->
  69. digraph_utils:subgraph(Graph, Vertices).
  70. -spec names_to_apps([atom()], [rebar_app_info:t()]) -> [rebar_app_info:t()].
  71. names_to_apps(Names, Apps) ->
  72. [element(2, App) || App <- [find_app_by_name(Name, Apps) || Name <- Names], App =/= error].
  73. -spec find_app_by_name(atom(), [rebar_app_info:t()]) -> {ok, rebar_app_info:t()} | error.
  74. find_app_by_name(Name, Apps) ->
  75. ec_lists:find(fun(App) ->
  76. rebar_app_info:name(App) =:= Name
  77. end, Apps).
  78. %% The union of all entries in the applications list for an app and
  79. %% the deps listed in its rebar.config is all deps that may be needed
  80. %% for building the app.
  81. all_apps_deps(App) ->
  82. Applications = lists:usort([atom_to_binary(X, utf8) || X <- rebar_app_info:applications(App)]),
  83. Deps = lists:usort(lists:map(fun({Name, _}) -> Name; (Name) -> Name end, rebar_app_info:deps(App))),
  84. lists:umerge(Deps, Applications).