From dfa668f21102ef5ac4cc5c2384e91b8d256513b5 Mon Sep 17 00:00:00 2001 From: Tristan Sloughter Date: Sat, 26 Sep 2015 14:01:06 -0500 Subject: only upgrade children and transitive children of dep being upgraded This commit replaces the method of upgrading by unlocking all transitive deps by one that utilizes the parent element of each app to only unlock transitive deps of children of the top level deps being upgraded. Additionally the run function of upgrade_SUITE is modified to only create the mock updates before the upgrade provider is run, instead of before any provider is run, which would cause improper behavior in install_deps. --- src/rebar_prv_upgrade.erl | 62 ++++++++++++++++++++++++++++++-------------- test/rebar_upgrade_SUITE.erl | 10 +++++-- 2 files changed, 51 insertions(+), 21 deletions(-) diff --git a/src/rebar_prv_upgrade.erl b/src/rebar_prv_upgrade.erl index 97d1953..476d62f 100644 --- a/src/rebar_prv_upgrade.erl +++ b/src/rebar_prv_upgrade.erl @@ -47,7 +47,8 @@ do(State) -> Locks = rebar_state:get(State, {locks, default}, []), Deps = rebar_state:get(State, deps, []), Names = parse_names(ec_cnv:to_binary(proplists:get_value(package, Args, <<"">>)), Locks), - case prepare_locks(Names, Deps, Locks, []) of + DepsDict = deps_dict(rebar_state:all_deps(State)), + case prepare_locks(Names, Deps, Locks, [], DepsDict) of {error, Reason} -> {error, Reason}; {Locks0, _Unlocks0} -> @@ -92,54 +93,77 @@ parse_names(Bin, Locks) -> Other -> Other end. -prepare_locks([], _, Locks, Unlocks) -> +prepare_locks([], _, Locks, Unlocks, _Dict) -> {Locks, Unlocks}; -prepare_locks([Name|Names], Deps, Locks, Unlocks) -> +prepare_locks([Name|Names], Deps, Locks, Unlocks, Dict) -> AtomName = binary_to_atom(Name, utf8), case lists:keyfind(Name, 1, Locks) of {_, _, 0} = Lock -> case rebar_utils:tup_find(AtomName, Deps) of false -> ?WARN("Dependency ~s has been removed and will not be upgraded", [Name]), - prepare_locks(Names, Deps, Locks, Unlocks); + prepare_locks(Names, Deps, Locks, Unlocks, Dict); Dep -> - {Source, NewLocks, NewUnlocks} = prepare_lock(Dep, Lock, Locks), + {Source, NewLocks, NewUnlocks} = prepare_lock(Dep, Lock, Locks, Dict), prepare_locks(Names, Deps, NewLocks, - [{Name, Source, 0} | NewUnlocks ++ Unlocks]) + [{Name, Source, 0} | NewUnlocks ++ Unlocks], Dict) end; {_, _, Level} = Lock when Level > 0 -> case rebar_utils:tup_find(AtomName, Deps) of false -> ?PRV_ERROR({transitive_dependency, Name}); Dep -> % Dep has been promoted - {Source, NewLocks, NewUnlocks} = prepare_lock(Dep, Lock, Locks), + {Source, NewLocks, NewUnlocks} = prepare_lock(Dep, Lock, Locks, Dict), prepare_locks(Names, Deps, NewLocks, - [{Name, Source, 0} | NewUnlocks ++ Unlocks]) + [{Name, Source, 0} | NewUnlocks ++ Unlocks], Dict) end; false -> ?PRV_ERROR({unknown_dependency, Name}) end. -prepare_lock(Dep, Lock, Locks) -> - Source = case Dep of - {_, SrcOrVsn} -> SrcOrVsn; - {_, _, Src} -> Src; +prepare_lock(Dep, Lock, Locks, Dict) -> + {Name1, Source} = case Dep of + {Name, SrcOrVsn} -> {Name, SrcOrVsn}; + {Name, _, Src} -> {Name, Src}; _ when is_atom(Dep) -> %% version-free package. Must unlock whatever matches in locks {_, Vsn, _} = lists:keyfind(ec_cnv:to_binary(Dep), 1, Locks), - Vsn + {Dep, Vsn} end, - {NewLocks, NewUnlocks} = unlock_higher_than(0, Locks -- [Lock]), + Children = all_children(Name1, Dict), + {NewLocks, NewUnlocks} = unlock_children(Children, Locks -- [Lock]), {Source, NewLocks, NewUnlocks}. top_level_deps(Deps, Locks) -> [Dep || Dep <- Deps, lists:keymember(0, 3, Locks)]. -unlock_higher_than(Level, Locks) -> unlock_higher_than(Level, Locks, [], []). +unlock_children(Children, Locks) -> + unlock_children(Children, Locks, [], []). -unlock_higher_than(_, [], Locks, Unlocks) -> +unlock_children(_, [], Locks, Unlocks) -> {Locks, Unlocks}; -unlock_higher_than(Level, [App = {_,_,AppLevel} | Apps], Locks, Unlocks) -> - if AppLevel > Level -> unlock_higher_than(Level, Apps, Locks, [App | Unlocks]); - AppLevel =< Level -> unlock_higher_than(Level, Apps, [App | Locks], Unlocks) +unlock_children(Children, [App = {Name,_,_} | Apps], Locks, Unlocks) -> + case lists:member(ec_cnv:to_binary(Name), Children) of + true -> + unlock_children(Children, Apps, Locks, [App | Unlocks]); + false -> + unlock_children(Children, Apps, [App | Locks], Unlocks) + end. + +deps_dict(Deps) -> + lists:foldl(fun(App, Dict) -> + Name = rebar_app_info:name(App), + Parent = rebar_app_info:parent(App), + dict:append_list(Parent, [Name], Dict) + end, dict:new(), Deps). + +all_children(Name, Dict) -> + lists:flatten(all_children_(Name, Dict)). + +all_children_(Name, Dict) -> + case dict:find(ec_cnv:to_binary(Name), Dict) of + {ok, Children} -> + Children ++ [all_children_(Child, Dict) || Child <- Children]; + error -> + [] end. diff --git a/test/rebar_upgrade_SUITE.erl b/test/rebar_upgrade_SUITE.erl index 54f16da..d9cd49a 100644 --- a/test/rebar_upgrade_SUITE.erl +++ b/test/rebar_upgrade_SUITE.erl @@ -561,13 +561,19 @@ run(Config) -> {error, Term} -> {error, Term}; _ -> {ok, Unlocks} end, - apply(?config(mock_update, Config), []), + + meck:new(rebar_prv_upgrade, [passthrough]), + meck:expect(rebar_prv_upgrade, do, fun(S) -> + apply(?config(mock_update, Config), []), + meck:passthrough([S]) + end), NewRebarConf = rebar_test_utils:create_config(?config(apps, Config), [{deps, ?config(next_top_deps, Config)}]), {ok, NewRebarConfig} = file:consult(NewRebarConf), rebar_test_utils:run_and_check( Config, NewRebarConfig, ["upgrade", App], Expectation - ). + ), + meck:unload(rebar_prv_upgrade). novsn_pkg(Config) -> apply(?config(mock, Config), []), -- cgit v1.1