summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/rebar_app_discover.erl7
-rw-r--r--src/rebar_config.erl36
-rw-r--r--src/rebar_digraph.erl44
-rw-r--r--src/rebar_prv_deps.erl35
-rw-r--r--src/rebar_prv_install_deps.erl4
-rw-r--r--src/rebar_utils.erl26
6 files changed, 120 insertions, 32 deletions
diff --git a/src/rebar_app_discover.erl b/src/rebar_app_discover.erl
index a350235..c5a79a6 100644
--- a/src/rebar_app_discover.erl
+++ b/src/rebar_app_discover.erl
@@ -61,7 +61,7 @@ merge_deps(AppInfo, State) ->
State1 = lists:foldl(fun(Profile, StateAcc) ->
AppProfDeps = rebar_state:get(AppState, {deps, Profile}, []),
TopLevelProfDeps = rebar_state:get(StateAcc, {deps, Profile}, []),
- ProfDeps2 = dedup(rebar_utils:tup_umerge(
+ ProfDeps2 = rebar_utils:tup_dedup(rebar_utils:tup_umerge(
rebar_utils:tup_sort(TopLevelProfDeps)
,rebar_utils:tup_sort(AppProfDeps))),
rebar_state:set(StateAcc, {deps, Profile}, ProfDeps2)
@@ -156,11 +156,6 @@ create_app_info(AppDir, AppFile) ->
end,
rebar_app_info:dir(rebar_app_info:valid(AppInfo1, Valid), AppDir).
-dedup([]) -> [];
-dedup([A]) -> [A];
-dedup([H,H|T]) -> dedup([H|T]);
-dedup([H|T]) -> [H|dedup(T)].
-
%% Read in and parse the .app file if it is availabe. Do the same for
%% the .app.src file if it exists.
try_handle_app_file([], AppDir, [], AppSrcScriptFile, Validate) ->
diff --git a/src/rebar_config.erl b/src/rebar_config.erl
index d2b4a12..554b399 100644
--- a/src/rebar_config.erl
+++ b/src/rebar_config.erl
@@ -124,27 +124,37 @@ bs(Vars) ->
%% Find deps that have been added to the config after the lock was created
find_newly_added(ConfigDeps, LockedDeps) ->
- rebar_utils:filtermap(fun(Dep) when is_tuple(Dep) ->
- check_newly_added(element(1, Dep), LockedDeps);
- (Dep) ->
- check_newly_added(Dep, LockedDeps)
- end, ConfigDeps).
-
-check_newly_added(Dep, LockedDeps) when is_atom(Dep) ->
- NewDep = ec_cnv:to_binary(Dep),
- case lists:keyfind(NewDep, 1, LockedDeps) of
+ [D || {true, D} <- [check_newly_added(Dep, LockedDeps) || Dep <- ConfigDeps]].
+
+check_newly_added({_, _}=Dep, LockedDeps) ->
+ check_newly_added_(Dep, LockedDeps);
+check_newly_added({Name, _, Source}, LockedDeps) ->
+ check_newly_added_({Name, Source}, LockedDeps);
+check_newly_added(Dep, LockedDeps) ->
+ check_newly_added_(Dep, LockedDeps).
+
+check_newly_added_({Name, Source}, LockedDeps) ->
+ case check_newly_added_(Name, LockedDeps) of
+ {true, Name1} ->
+ {true, {Name1, Source}};
false ->
- true;
+ false
+ end;
+check_newly_added_(Dep, LockedDeps) when is_atom(Dep) ->
+ Name = ec_cnv:to_binary(Dep),
+ case lists:keyfind(Name, 1, LockedDeps) of
+ false ->
+ {true, Name};
Match ->
case element(3, Match) of
0 ->
- true;
+ {true, Name};
_ ->
?WARN("Newly added dep ~s is locked at a lower level. "
"If you really want to unlock it, use 'rebar3 upgrade ~s'",
- [NewDep, NewDep]),
+ [Name, Name]),
false
end
end;
-check_newly_added(Dep, _) ->
+check_newly_added_(Dep, _) ->
throw(?PRV_ERROR({bad_dep_name, Dep})).
diff --git a/src/rebar_digraph.erl b/src/rebar_digraph.erl
index a4635e3..e989fdc 100644
--- a/src/rebar_digraph.erl
+++ b/src/rebar_digraph.erl
@@ -3,7 +3,9 @@
-export([compile_order/1
,restore_graph/1
,cull_deps/3
+ ,cull_deps/4
,subgraph/2
+ ,print_solution/2
,format_error/1]).
-include("rebar.hrl").
@@ -68,7 +70,12 @@ restore_graph({Vs, Es}) ->
%% Pick packages to fullfill dependencies
%% The first dep while traversing the graph is chosen and any conflicting
%% dep encountered later on is ignored.
+
cull_deps(Graph, Vertices, Level) ->
+ {ok, LvlVertices, Discarded, _} = cull_deps(Graph, Vertices, Level, none),
+ {ok, LvlVertices, Discarded}.
+
+cull_deps(Graph, Vertices, Level, SolutionGraph) ->
cull_deps(Graph,
Vertices,
Level+1,
@@ -78,13 +85,14 @@ cull_deps(Graph, Vertices, Level) ->
lists:foldl(fun({Key, _}=N, Solution) ->
dict:store(Key, N, Solution)
end, dict:new(), Vertices),
- []).
+ [],
+ SolutionGraph).
-cull_deps(_Graph, [], _Level, Levels, Solution, Discarded) ->
+cull_deps(_Graph, [], _Level, Levels, Solution, Discarded, SolutionGraph) ->
{_, Vertices} = lists:unzip(dict:to_list(Solution)),
LvlVertices = [{App,Vsn,dict:fetch(App,Levels)} || {App,Vsn} <- Vertices],
- {ok, LvlVertices, Discarded};
-cull_deps(Graph, Vertices, Level, Levels, Solution, Discarded) ->
+ {ok, LvlVertices, Discarded, SolutionGraph};
+cull_deps(Graph, Vertices, Level, Levels, Solution, Discarded, SolutionGraph) ->
{NV, NS, LS, DS} =
lists:foldl(fun(V, {NewVertices, SolutionAcc, LevelsAcc, DiscardedAcc}) ->
OutNeighbors = lists:keysort(1, digraph:out_neighbours(Graph, V)),
@@ -95,6 +103,7 @@ cull_deps(Graph, Vertices, Level, Levels, Solution, Discarded) ->
{ok, _} -> % conflict resolution!
{NewVertices1, SolutionAcc1, LevelsAcc1, [N|DiscardedAcc1]};
error ->
+ add_to_solution_graph(N, V, SolutionGraph),
{[N | NewVertices1],
dict:store(Key, N, SolutionAcc1),
dict:store(Key, Level, LevelsAcc1),
@@ -102,11 +111,36 @@ cull_deps(Graph, Vertices, Level, Levels, Solution, Discarded) ->
end
end, {NewVertices, SolutionAcc, LevelsAcc, DiscardedAcc}, OutNeighbors)
end, {[], Solution, Levels, Discarded}, lists:keysort(1, Vertices)),
- cull_deps(Graph, NV, Level+1, LS, NS, DS).
+ cull_deps(Graph, NV, Level+1, LS, NS, DS, SolutionGraph).
subgraph(Graph, Vertices) ->
digraph_utils:subgraph(Graph, Vertices).
+add_to_solution_graph(_, _, none) ->
+ ok;
+add_to_solution_graph(N, V, SolutionGraph) ->
+ NewV = digraph:add_vertex(SolutionGraph, N),
+ digraph:add_edge(SolutionGraph, V, NewV).
+
+print_solution(Graph, Deps) ->
+ SolutionGraph = digraph:new(),
+ [digraph:add_vertex(SolutionGraph, V) || V <- Deps],
+ cull_deps(Graph, Deps, 0, SolutionGraph),
+ print_solution(SolutionGraph, Deps, 0).
+
+print_solution(_, [], _) ->
+ ok;
+print_solution(SolutionGraph, [{N, V} | Vertices], 0) ->
+ ?CONSOLE("~s-~s", [N, V]),
+ OutNeighbors = lists:keysort(1, digraph:out_neighbours(SolutionGraph, {N,V})),
+ print_solution(SolutionGraph, OutNeighbors, 4),
+ print_solution(SolutionGraph, Vertices, 0);
+print_solution(SolutionGraph, [{N, V} | Vertices], Indent) ->
+ ?CONSOLE("~s~s-~s", [[" " || _ <- lists:seq(0, Indent)], N, V]),
+ OutNeighbors = lists:keysort(1, digraph:out_neighbours(SolutionGraph, {N,V})),
+ print_solution(SolutionGraph, OutNeighbors, Indent+4),
+ print_solution(SolutionGraph, Vertices, Indent).
+
format_error(no_solution) ->
io_lib:format("No solution for packages found.", []).
diff --git a/src/rebar_prv_deps.erl b/src/rebar_prv_deps.erl
index be81c31..8f065db 100644
--- a/src/rebar_prv_deps.erl
+++ b/src/rebar_prv_deps.erl
@@ -24,16 +24,25 @@ init(State) ->
{short_desc, "List dependencies"},
{desc, "List dependencies. Those not matching lock files "
"are followed by an asterisk (*)."},
- {opts, []}])),
+ {opts, [{tree, $t, "tree", undefined, "Display dependencies in tree format."}]}])),
{ok, State1}.
-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
- Profiles = rebar_state:current_profiles(State),
- List = [{Profile, rebar_state:get(State, {deps, Profile}, [])}
- || Profile <- Profiles],
- [display(State, Profile, Deps) || {Profile, Deps} <- List],
- {ok, State}.
+ case display_tree(State) of
+ true ->
+ {_Packages, Graph} = rebar_state:packages(State),
+ List = merge_deps_per_profile(State),
+ {_SrcDeps, PkgDeps} = rebar_prv_install_deps:parse_deps("", List, State, [], 0),
+ rebar_digraph:print_solution(Graph, PkgDeps),
+ {ok, State};
+ false ->
+ Profiles = rebar_state:current_profiles(State),
+ List = [{Profile, rebar_state:get(State, {deps, Profile}, [])}
+ || Profile <- Profiles],
+ [display(State, Profile, Deps) || {Profile, Deps} <- List],
+ {ok, State}
+ end.
-spec format_error(any()) -> iolist().
format_error(Reason) ->
@@ -116,3 +125,17 @@ display_dep(State, {Name, Source, Level}) when is_tuple(Source), is_integer(Leve
?CONSOLE("~s~s (locked ~s source)", [Name, NeedsUpdate, type(Source)]).
type(Source) when is_tuple(Source) -> element(1, Source).
+
+display_tree(State) ->
+ {Args, _} = rebar_state:command_parsed_args(State),
+ proplists:get_value(tree, Args, false).
+
+merge_deps_per_profile(State) ->
+ Profiles = rebar_state:current_profiles(State),
+ lists:foldl(fun(Profile, Deps) ->
+ D = rebar_utils:deps_to_binary(rebar_state:get(State, {deps, Profile}, [])),
+ D1 = rebar_utils:tup_sort(D),
+ rebar_utils:tup_dedup(
+ rebar_utils:tup_umerge(D1
+ ,Deps))
+ end, [], Profiles).
diff --git a/src/rebar_prv_install_deps.erl b/src/rebar_prv_install_deps.erl
index 73d002a..b590d03 100644
--- a/src/rebar_prv_install_deps.erl
+++ b/src/rebar_prv_install_deps.erl
@@ -36,6 +36,7 @@
-include_lib("providers/include/providers.hrl").
-export([handle_deps_as_profile/4,
+ parse_deps/5,
find_cycles/1,
cull_compile/2]).
@@ -122,7 +123,6 @@ handle_deps_as_profile(Profile, State, Deps, Upgrade) ->
Locks = [],
Level = 0,
DepsDir = profile_dep_dir(State, Profile),
-
{SrcDeps, PkgDeps} = parse_deps(DepsDir, Deps, State, Locks, Level),
AllSrcProfileDeps = [{Profile, SrcDeps, Locks, Level}],
AllPkgProfileDeps = [{Profile, Locks, PkgDeps, Level}],
@@ -511,7 +511,7 @@ parse_dep({Name, Vsn}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is
{SrcDepsAcc, [parse_goal(ec_cnv:to_binary(Name)
,ec_cnv:to_binary(Vsn)) | PkgDepsAcc]}
end;
-parse_dep(Name, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_atom(Name) ->
+parse_dep(Name, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_atom(Name); is_binary(Name) ->
%% Unversioned package dependency
{PkgName, PkgVsn} = get_package(ec_cnv:to_binary(Name), State),
CheckoutsDir = ec_cnv:to_list(rebar_dir:checkouts_dir(State, Name)),
diff --git a/src/rebar_utils.erl b/src/rebar_utils.erl
index 30da523..c7128fc 100644
--- a/src/rebar_utils.erl
+++ b/src/rebar_utils.erl
@@ -54,6 +54,8 @@
expand_env_variable/3,
get_arch/0,
wordsize/0,
+ deps_to_binary/1,
+ tup_dedup/1,
tup_umerge/2,
tup_sort/1,
tup_find/2,
@@ -236,6 +238,30 @@ erl_opts(Config) ->
%% was enclosed in quotes and might have commas but should not be split.
args_to_tasks(Args) -> new_task(Args, []).
+deps_to_binary([]) ->
+ [];
+deps_to_binary([{Name, _, Source} | T]) ->
+ [{ec_cnv:to_binary(Name), Source} | deps_to_binary(T)];
+deps_to_binary([{Name, Source} | T]) ->
+ [{ec_cnv:to_binary(Name), Source} | deps_to_binary(T)];
+deps_to_binary([Name | T]) ->
+ [ec_cnv:to_binary(Name) | deps_to_binary(T)].
+
+tup_dedup([]) ->
+ [];
+tup_dedup([A]) ->
+ [A];
+tup_dedup([A,B|T]) when element(1, A) =:= element(1, B) ->
+ tup_dedup([A | T]);
+tup_dedup([A,B|T]) when element(1, A) =:= B ->
+ tup_dedup([A | T]);
+tup_dedup([A,B|T]) when A =:= element(1, B) ->
+ tup_dedup([A | T]);
+tup_dedup([A,A|T]) ->
+ [A|tup_dedup(T)];
+tup_dedup([A|T]) ->
+ [A|tup_dedup(T)].
+
%% Sort the list in proplist-order, meaning that `{a,b}' and `{a,c}'
%% both compare as usual, and `a' and `b' do the same, but `a' and `{a,b}' will
%% compare based on the first element of the key, and in order. So the following