diff options
-rw-r--r-- | src/rebar_app_discover.erl | 7 | ||||
-rw-r--r-- | src/rebar_config.erl | 36 | ||||
-rw-r--r-- | src/rebar_digraph.erl | 44 | ||||
-rw-r--r-- | src/rebar_prv_deps.erl | 35 | ||||
-rw-r--r-- | src/rebar_prv_install_deps.erl | 4 | ||||
-rw-r--r-- | src/rebar_utils.erl | 26 |
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..dc356a8 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 package dependencies in tree format (git and hg deps not supported)."}]}])), {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 |