anchor.diff:                      rlx_app_info:new(kernel, "0.1", "/no-dir", [], []),
                       rlx_app_info:new(zapp2, "0.1", "/no-dir", [], [])]],
       {ok, Sorted} = sort_apps(Apps),
  -    ?assertMatch([stdlib, kernel, zapp2,
  -                  app3, app2, zapp1, app1],
  +    ?assertMatch([kernel, app3, app2, zapp2, zapp1, stdlib, app1],
  +                 [rlx_app_info:name(App) || App <- Sorted]).
  +
  +topo_apps_1_test() ->
  +    Apps = [App ||
  +               {ok, App} <-
  +                   [rlx_app_info:new(app0, "0.1", "/no-dir", [], [stdlib, dep1, dep2, dep3]),
  +                    rlx_app_info:new(app1, "0.1", "/no-dir", [], [stdlib, kernel]),
  +                    rlx_app_info:new(dep1, "0.1", "/no-dir", [], []),
  +                    rlx_app_info:new(dep2, "0.1", "/no-dir", [], []),
  +                    rlx_app_info:new(dep3, "0.1", "/no-dir", [], []),
  +                    rlx_app_info:new(stdlib, "0.1", "/no-dir", [], []),
  +                    rlx_app_info:new(kernel, "0.1", "/no-dir", [], [])]],
  +    {ok, Sorted} = sort_apps(Apps),
  +    ?assertMatch([stdlib, dep1, dep2, dep3, app0, kernel, app1],
                    [rlx_app_info:name(App) || App <- Sorted]).
   
   topo_apps_2_test() ->
  
positive.code: %%%  A topological sort of a partial order is a sequence of elements
  %%%  [X1, X2, X3 ...] such that if whenever {Xi, Xj} is in the partial
  %%%  order i &lt; j
  %%% @end
  %%%-------------------------------------------------------------------
  -module(rlx_topo).
  
  -export([sort/1,
           sort_apps/1,
           format_error/1]).
  
  -include("relx.hrl").
  
  %%====================================================================
  %% Types
  %%====================================================================
  -type pair() :: {DependentApp::atom(), PrimaryApp::atom()}.
  -type name() :: AppName::atom().
  -type element() :: name() | pair().
  
  %%====================================================================
  %% API
  
negative.code:                        {ok, [rlx_app_info:t()]} |
                         relx:error().
  sort_apps(Apps) ->
      AppDeps = [{rlx_app_info:name(App),
                  rlx_app_info:active_deps(App) ++ rlx_app_info:library_deps(App)}
                 || App <- Apps],
      {AppNames, _} = lists:unzip(AppDeps),
      case lists:foldl(fun iterator/2, {ok, [], AppDeps, []}, AppNames) of
          {ok, Names, _, _} ->
              {ok, names_to_apps(lists:reverse(Names), Apps)};
          E ->
              E
      end.
  
  %% @doc nicely format the error from the sort.
  -spec format_error(Reason::term()) -> iolist().
  format_error({cycle, App, Path}) ->
      ["Cycle detected in dependency graph, this must be resolved "
       "before we can continue:\n",
       rlx_util:indent(2),
       [[erlang:atom_to_list(A), " -> "] || A <- lists:reverse(Path)],
       erlang:atom_to_list(App)].
  
  %%====================================================================
  %% Internal Functions
  %%====================================================================
  
  -type name() :: AppName::atom().
  -type app_dep() :: {AppName::name(), [DepName::name()]}.
  -type iterator_state() :: {ok, [Acc::name()],
                             [Apps::app_dep()],
                             [Path::name()]}.
  
  -spec iterator(name(), iterator_state() | relx:error()) ->
                        iterator_state() | relx:error().
  iterator(App, {ok, Acc, Apps, Path}) ->
      case lists:member(App, Acc) of
          false ->
              %% haven't seen this app yet
              case lists:keytake(App, 1, Apps) of
                  {value, {App, Deps}, NewApps} ->
                      DepInit = {ok, Acc, NewApps, [App | Path]},
                      %% recurse over deps
                      case lists:foldl(fun iterator/2, DepInit, Deps) of
                          {ok, DepAcc, DepApps, _} ->
                              {ok, [App | DepAcc], DepApps, Path};
                          Error ->
                              Error
                      end;
                  false ->
                      %% we have visited this app before,
                      %% that means there's a cycle
                      ?RLX_ERROR({cycle, App, Path})
              end;
          true ->
              %% this app and its deps were already processed
              {ok, Acc, Apps, Path}
      end;
  iterator(_, Error) ->
      Error.
  
  -spec names_to_apps([atom()], [rlx_app_info:t()]) -> [rlx_app_info:t()].
  names_to_apps(Names, Apps) ->
   [find_app_by_name(Name, Apps) || Name <- Names].
  