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.

114 regels
4.5 KiB

10 jaren geleden
10 jaren geleden
10 jaren geleden
10 jaren geleden
10 jaren geleden
10 jaren geleden
10 jaren geleden
10 jaren geleden
10 jaren geleden
  1. %%% @doc build a digraph of applications in order to figure out dependency
  2. %%% and compile order.
  3. -module(rebar_digraph).
  4. -export([compile_order/1
  5. ,restore_graph/1
  6. ,subgraph/2
  7. ,format_error/1]).
  8. -include("rebar.hrl").
  9. %% @doc Sort apps with topological sort to get proper build order
  10. -spec compile_order([rebar_app_info:t()]) ->
  11. {ok, [rebar_app_info:t()]} | {error, no_sort | {cycles, [[binary(),...]]}}.
  12. compile_order(Apps) ->
  13. Graph = digraph:new(),
  14. lists:foreach(fun(App) ->
  15. Name = rebar_app_info:name(App),
  16. Deps = all_apps_deps(App),
  17. add(Graph, {Name, Deps})
  18. end, Apps),
  19. Order =
  20. case digraph_utils:topsort(Graph) of
  21. false ->
  22. case digraph_utils:is_acyclic(Graph) of
  23. true ->
  24. {error, no_sort};
  25. false ->
  26. Cycles = lists:sort(
  27. [lists:sort(Comp) || Comp <- digraph_utils:strong_components(Graph),
  28. length(Comp)>1]),
  29. {error, {cycles, Cycles}}
  30. end;
  31. V ->
  32. {ok, names_to_apps(lists:reverse(V), Apps)}
  33. end,
  34. true = digraph:delete(Graph),
  35. Order.
  36. %% @private Add a package and its dependencies to an existing digraph
  37. -spec add(digraph:graph(), {PkgName, [Dep]}) -> ok when
  38. PkgName :: binary(),
  39. Dep :: {Name, term()} | Name,
  40. Name :: atom() | iodata().
  41. add(Graph, {PkgName, Deps}) ->
  42. case digraph:vertex(Graph, PkgName) of
  43. false ->
  44. V = digraph:add_vertex(Graph, PkgName);
  45. {V, []} ->
  46. V
  47. end,
  48. lists:foreach(fun(DepName) ->
  49. Name1 = case DepName of
  50. {Name, _Vsn} ->
  51. rebar_utils:to_binary(Name);
  52. Name ->
  53. rebar_utils:to_binary(Name)
  54. end,
  55. V3 = case digraph:vertex(Graph, Name1) of
  56. false ->
  57. digraph:add_vertex(Graph, Name1);
  58. {V2, []} ->
  59. V2
  60. end,
  61. digraph:add_edge(Graph, V, V3)
  62. end, Deps).
  63. %% @doc based on a list of vertices and edges, build a digraph.
  64. -spec restore_graph({[digraph:vertex()], [digraph:edge()]}) -> digraph:graph().
  65. restore_graph({Vs, Es}) ->
  66. Graph = digraph:new(),
  67. lists:foreach(fun({V, LastUpdated}) ->
  68. digraph:add_vertex(Graph, V, LastUpdated)
  69. end, Vs),
  70. lists:foreach(fun({V1, V2}) ->
  71. digraph:add_edge(Graph, V1, V2)
  72. end, Es),
  73. Graph.
  74. %% @doc convert a given exception's payload into an io description.
  75. -spec format_error(any()) -> iolist().
  76. format_error(no_solution) ->
  77. io_lib:format("No solution for packages found.", []).
  78. %%====================================================================
  79. %% Internal Functions
  80. %%====================================================================
  81. %% @doc alias for `digraph_utils:subgraph/2'.
  82. subgraph(Graph, Vertices) ->
  83. digraph_utils:subgraph(Graph, Vertices).
  84. %% @private from a list of app names, fetch the proper app info records
  85. %% for them.
  86. -spec names_to_apps([atom()], [rebar_app_info:t()]) -> [rebar_app_info:t()].
  87. names_to_apps(Names, Apps) ->
  88. [element(2, App) || App <- [find_app_by_name(Name, Apps) || Name <- Names], App =/= error].
  89. %% @private fetch the proper app info record for a given app name.
  90. -spec find_app_by_name(atom(), [rebar_app_info:t()]) -> {ok, rebar_app_info:t()} | error.
  91. find_app_by_name(Name, Apps) ->
  92. ec_lists:find(fun(App) ->
  93. rebar_app_info:name(App) =:= Name
  94. end, Apps).
  95. %% @private The union of all entries in the applications list for an app and
  96. %% the deps listed in its rebar.config is all deps that may be needed
  97. %% for building the app.
  98. -spec all_apps_deps(rebar_app_info:t()) -> [binary()].
  99. all_apps_deps(App) ->
  100. Applications = lists:usort([atom_to_binary(X, utf8) || X <- rebar_app_info:applications(App)]),
  101. Deps = lists:usort(lists:map(fun({Name, _}) -> Name; (Name) -> Name end, rebar_app_info:deps(App))),
  102. lists:umerge(Deps, Applications).