/* vim: set sw=4 sts=4 et foldmethod=syntax : *//* * Copyright (c) 2009, 2010 Ciaran McCreesh * * This file is part of the Paludis package manager. Paludis is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */#include<paludis/resolver/decider.hh>#include<paludis/resolver/resolver_functions.hh>#include<paludis/resolver/spec_rewriter.hh>#include<paludis/resolver/resolvent.hh>#include<paludis/resolver/resolution.hh>#include<paludis/resolver/constraint.hh>#include<paludis/resolver/decision.hh>#include<paludis/resolver/destination.hh>#include<paludis/resolver/resolutions_by_resolvent.hh>#include<paludis/resolver/suggest_restart.hh>#include<paludis/resolver/reason.hh>#include<paludis/resolver/unsuitable_candidates.hh>#include<paludis/resolver/resolver.hh>#include<paludis/resolver/required_confirmations.hh>#include<paludis/resolver/change_by_resolvent.hh>#include<paludis/resolver/collect_depped_upon.hh>#include<paludis/resolver/collect_installed.hh>#include<paludis/resolver/collect_purges.hh>#include<paludis/resolver/accumulate_deps_and_provides.hh>#include<paludis/resolver/why_changed_choices.hh>#include<paludis/util/exception.hh>#include<paludis/util/stringify.hh>#include<paludis/util/make_named_values.hh>#include<paludis/util/make_shared_copy.hh>#include<paludis/util/wrapped_forward_iterator.hh>#include<paludis/util/wrapped_output_iterator.hh>#include<paludis/util/enum_iterator.hh>#include<paludis/util/indirect_iterator-impl.hh>#include<paludis/util/tribool.hh>#include<paludis/util/log.hh>#include<paludis/util/simple_visitor_cast.hh>#include<paludis/util/make_null_shared_ptr.hh>#include<paludis/util/accept_visitor.hh>#include<paludis/environment.hh>#include<paludis/notifier_callback.hh>#include<paludis/repository.hh>#include<paludis/filtered_generator.hh>#include<paludis/package_database.hh>#include<paludis/metadata_key.hh>#include<paludis/generator.hh>#include<paludis/selection.hh>#include<paludis/filter.hh>#include<paludis/match_package.hh>#include<paludis/version_requirements.hh>#include<paludis/slot_requirement.hh>#include<paludis/choice.hh>#include<paludis/action.hh>#include<paludis/elike_slot_requirement.hh>#include<paludis/package_id.hh>#include<paludis/changed_choices.hh>#include<paludis/additional_package_dep_spec_requirement.hh>#include<paludis/partially_made_package_dep_spec.hh>#include<paludis/util/pimp-impl.hh>#include<algorithm>#include<map>#include<set>usingnamespacepaludis;usingnamespacepaludis::resolver;namespacepaludis{template<>structImp<Decider>{constEnvironment*constenv;constResolverFunctionsfns;SpecRewriterrewriter;conststd::shared_ptr<ResolutionsByResolvent>resolutions_by_resolvent;Imp(constEnvironment*conste,constResolverFunctions&f,conststd::shared_ptr<ResolutionsByResolvent>&l):env(e),fns(f),rewriter(env),resolutions_by_resolvent(l){}};}Decider::Decider(constEnvironment*conste,constResolverFunctions&f,conststd::shared_ptr<ResolutionsByResolvent>&l):Pimp<Decider>(e,f,l){}Decider::~Decider(){}voidDecider::_resolve_decide_with_dependencies(){Contextcontext("When resolving and adding dependencies recursively:");enumState{deciding_non_suggestions,deciding_nothings,deciding_suggestions,finished}state=deciding_non_suggestions;boolchanged(true);while(true){if(!changed)state=State(state+1);if(state==finished)break;changed=false;for(ResolutionsByResolvent::ConstIteratori(_imp->resolutions_by_resolvent->begin()),i_end(_imp->resolutions_by_resolvent->end());i!=i_end;++i){/* we've already decided */if((*i)->decision())continue;/* we're only being suggested. don't do this on the first pass, so * we don't have to do restarts for suggestions later becoming hard * deps. */if(state<deciding_suggestions&&(*i)->constraints()->all_untaken())continue;/* avoid deciding nothings until after we've decided things we've * taken, so adding extra destinations doesn't get messy. */if(state<deciding_nothings&&(*i)->constraints()->nothing_is_fine_too())continue;_imp->env->trigger_notifier_callback(NotifierCallbackResolverStepEvent());changed=true;_decide(*i);_add_dependencies_if_necessary(*i);}}}boolDecider::_resolve_vias(){Contextcontext("When finding vias:");boolchanged(false);for(ResolutionsByResolvent::ConstIteratori(_imp->resolutions_by_resolvent->begin()),i_end(_imp->resolutions_by_resolvent->end());i!=i_end;++i){if((*i)->resolvent().destination_type()==dt_create_binary)continue;if(!_always_via_binary(*i))continue;_imp->env->trigger_notifier_callback(NotifierCallbackResolverStepEvent());Resolventbinary_resolvent((*i)->resolvent());binary_resolvent.destination_type()=dt_create_binary;conststd::shared_ptr<Resolution>binary_resolution(_resolution_for_resolvent(binary_resolvent,true));boolalready(false);for(Constraints::ConstIteratorc(binary_resolution->constraints()->begin()),c_end(binary_resolution->constraints()->end());c!=c_end;++c)if(simple_visitor_cast<constViaBinaryReason>(*(*c)->reason())){already=true;break;}if(already)continue;changed=true;conststd::shared_ptr<constConstraintSequence>constraints(_make_constraints_for_via_binary(binary_resolution,*i));for(ConstraintSequence::ConstIteratorc(constraints->begin()),c_end(constraints->end());c!=c_end;++c)_apply_resolution_constraint(binary_resolution,*c);_decide(binary_resolution);}returnchanged;}boolDecider::_always_via_binary(conststd::shared_ptr<constResolution>&resolution)const{return_imp->fns.always_via_binary_fn()(resolution);}conststd::shared_ptr<ConstraintSequence>Decider::_make_constraints_for_via_binary(conststd::shared_ptr<constResolution>&resolution,conststd::shared_ptr<constResolution>&other_resolution)const{return_imp->fns.get_constraints_for_via_binary_fn()(resolution,other_resolution);}boolDecider::_resolve_dependents(){Contextcontext("When finding dependents:");boolchanged(false);conststd::pair<std::shared_ptr<constChangeByResolventSequence>,std::shared_ptr<constChangeByResolventSequence>>changing(_collect_changing());if(changing.first->empty())returnfalse;conststd::shared_ptr<constPackageIDSequence>staying(_collect_staying(changing.first));for(PackageIDSequence::ConstIterators(staying->begin()),s_end(staying->end());s!=s_end;++s){_imp->env->trigger_notifier_callback(NotifierCallbackResolverStepEvent());if(!(*s)->supports_action(SupportsActionTest<UninstallAction>()))continue;conststd::shared_ptr<constChangeByResolventSequence>dependent_upon_ids(dependent_upon(_imp->env,*s,changing.first,changing.second,staying));if(dependent_upon_ids->empty())continue;Resolventresolvent(*s,dt_install_to_slash);boolremove(_remove_if_dependent(*s));/* we've changed things if we've not already done anything for this * resolvent, but only if we're going to remove it rather than mark it * as broken */if(remove&&_imp->resolutions_by_resolvent->end()==_imp->resolutions_by_resolvent->find(resolvent))changed=true;conststd::shared_ptr<Resolution>resolution(_resolution_for_resolvent(resolvent,true));conststd::shared_ptr<constConstraintSequence>constraints(_make_constraints_for_dependent(resolution,*s,dependent_upon_ids));for(ConstraintSequence::ConstIteratorc(constraints->begin()),c_end(constraints->end());c!=c_end;++c)_apply_resolution_constraint(resolution,*c);if((!remove)&&(!resolution->decision())){if(!_try_to_find_decision_for(resolution,false,false,false,false,false))resolution->decision()=std::make_shared<BreakDecision>(resolvent,*s,true);else{/* we'll do the actual deciding plus deps etc later */changed=true;}}}returnchanged;}conststd::shared_ptr<ConstraintSequence>Decider::_make_constraints_for_dependent(conststd::shared_ptr<constResolution>&resolution,conststd::shared_ptr<constPackageID>&id,conststd::shared_ptr<constChangeByResolventSequence>&r)const{return_imp->fns.get_constraints_for_dependent_fn()(resolution,id,r);}namespace{conststd::shared_ptr<constPackageID>get_change_by_resolvent_id(constChangeByResolvent&r){returnr.package_id();}}namespace{structChangingCollector{std::shared_ptr<Resolution>current_resolution;std::shared_ptr<ChangeByResolventSequence>going_away;std::shared_ptr<ChangeByResolventSequence>newly_available;ChangingCollector():going_away(std::make_shared<ChangeByResolventSequence>()),newly_available(std::make_shared<ChangeByResolventSequence>()){}voidvisit(constNothingNoChangeDecision&){}voidvisit(constExistingNoChangeDecision&){}voidvisit(constRemoveDecision&d){for(PackageIDSequence::ConstIteratori(d.ids()->begin()),i_end(d.ids()->end());i!=i_end;++i)going_away->push_back(make_named_values<ChangeByResolvent>(n::package_id()=*i,n::resolvent()=current_resolution->resolvent()));}voidvisit(constChangesToMakeDecision&d){for(PackageIDSequence::ConstIteratori(d.destination()->replacing()->begin()),i_end(d.destination()->replacing()->end());i!=i_end;++i)going_away->push_back(make_named_values<ChangeByResolvent>(n::package_id()=*i,n::resolvent()=current_resolution->resolvent()));newly_available->push_back(make_named_values<ChangeByResolvent>(n::package_id()=d.origin_id(),n::resolvent()=current_resolution->resolvent()));}voidvisit(constUnableToMakeDecision&){}voidvisit(constBreakDecision&){}};}conststd::pair<std::shared_ptr<constChangeByResolventSequence>,std::shared_ptr<constChangeByResolventSequence>>Decider::_collect_changing()const{ChangingCollectorc;for(ResolutionsByResolvent::ConstIteratori(_imp->resolutions_by_resolvent->begin()),i_end(_imp->resolutions_by_resolvent->end());i!=i_end;++i)if((*i)->decision()&&(*i)->decision()->taken()){c.current_resolution=*i;(*i)->decision()->accept(c);}returnstd::make_pair(c.going_away,c.newly_available);}namespace{structChangeByResolventPackageIDIs{conststd::shared_ptr<constPackageID>id;ChangeByResolventPackageIDIs(conststd::shared_ptr<constPackageID>&i):id(i){}booloperator()(constChangeByResolvent&r)const{return*r.package_id()==*id;}};}conststd::shared_ptr<constPackageIDSequence>Decider::_collect_staying(conststd::shared_ptr<constChangeByResolventSequence>&going_away)const{conststd::shared_ptr<constPackageIDSequence>existing((*_imp->env)[selection::AllVersionsUnsorted(generator::All()|filter::InstalledAtRoot(_imp->env->system_root_key()->value()))]);conststd::shared_ptr<PackageIDSequence>result(std::make_shared<PackageIDSequence>());for(PackageIDSequence::ConstIteratorx(existing->begin()),x_end(existing->end());x!=x_end;++x)if(going_away->end()==std::find_if(going_away->begin(),going_away->end(),ChangeByResolventPackageIDIs(*x)))result->push_back(*x);returnresult;}voidDecider::_resolve_confirmations(){Contextcontext("When resolving confirmations:");for(ResolutionsByResolvent::ConstIteratori(_imp->resolutions_by_resolvent->begin()),i_end(_imp->resolutions_by_resolvent->end());i!=i_end;++i)_confirm(*i);}voidDecider::_fixup_changes_to_make_decision(conststd::shared_ptr<constResolution>&resolution,ChangesToMakeDecision&decision)const{decision.set_destination(_make_destination_for(resolution,decision));decision.set_change_type(_make_change_type_for(resolution,decision));}conststd::shared_ptr<Destination>Decider::_make_destination_for(conststd::shared_ptr<constResolution>&resolution,constChangesToMakeDecision&decision)const{conststd::shared_ptr<constRepository>repo(_find_repository_for(resolution,decision));if((!repo->destination_interface())||(!repo->destination_interface()->is_suitable_destination_for(*decision.origin_id())))throwInternalError(PALUDIS_HERE,stringify(repo->name())+" is not a suitable destination for "+stringify(*decision.origin_id()));returnstd::make_shared<Destination>(make_named_values<Destination>(n::replacing()=_find_replacing(decision.origin_id(),repo),n::repository()=repo->name()));}constChangeTypeDecider::_make_change_type_for(conststd::shared_ptr<constResolution>&,constChangesToMakeDecision&decision)const{if(decision.destination()->replacing()->empty()){conststd::shared_ptr<constPackageIDSequence>others((*_imp->env)[selection::SomeArbitraryVersion(generator::Package(decision.origin_id()->name())&generator::InRepository(decision.destination()->repository()))]);if(others->empty())returnct_new;elsereturnct_slot_new;}else{/* we pick the worst, so replacing 1 and 3 with 2 requires permission to * downgrade */ChangeTyperesult(last_ct);for(PackageIDSequence::ConstIteratori(decision.destination()->replacing()->begin()),i_end(decision.destination()->replacing()->end());i!=i_end;++i){if((*i)->version()==decision.origin_id()->version())result=std::min(result,ct_reinstall);elseif((*i)->version()<decision.origin_id()->version())result=std::min(result,ct_upgrade);elseif((*i)->version()>decision.origin_id()->version())result=std::min(result,ct_downgrade);}returnresult;}}conststd::shared_ptr<constRepository>Decider::_find_repository_for(conststd::shared_ptr<constResolution>&resolution,constChangesToMakeDecision&decision)const{return_imp->fns.find_repository_for_fn()(resolution,decision);}FilteredGeneratorDecider::_make_destination_filtered_generator(constGenerator&g,conststd::shared_ptr<constResolution>&resolution)const{return_imp->fns.make_destination_filtered_generator_fn()(g,resolution);}FilteredGeneratorDecider::_make_origin_filtered_generator(constGenerator&g,conststd::shared_ptr<constResolution>&resolution)const{return_imp->fns.make_origin_filtered_generator_fn()(g,resolution);}FilterDecider::_make_unmaskable_filter(conststd::shared_ptr<constResolution>&resolution)const{return_imp->fns.make_unmaskable_filter_fn()(resolution);}boolDecider::_allow_choice_changes_for(conststd::shared_ptr<constResolution>&resolution)const{return_imp->fns.allow_choice_changes_fn()(resolution);}conststd::shared_ptr<constPackageIDSequence>Decider::_find_replacing(conststd::shared_ptr<constPackageID>&id,conststd::shared_ptr<constRepository>&repo)const{Contextcontext("When working out what is replaced by '"+stringify(*id)+"' when it is installed to '"+stringify(repo->name())+"':");std::set<RepositoryName>repos;if(repo->installed_root_key()){for(PackageDatabase::RepositoryConstIteratorr(_imp->env->package_database()->begin_repositories()),r_end(_imp->env->package_database()->end_repositories());r!=r_end;++r)if((*r)->installed_root_key()&&(*r)->installed_root_key()->value()==repo->installed_root_key()->value())repos.insert((*r)->name());}elserepos.insert(repo->name());std::shared_ptr<PackageIDSequence>result(std::make_shared<PackageIDSequence>());for(std::set<RepositoryName>::const_iteratorr(repos.begin()),r_end(repos.end());r!=r_end;++r){std::shared_ptr<constPackageIDSequence>ids((*_imp->env)[selection::AllVersionsUnsorted(generator::Package(id->name())&generator::InRepository(*r))]);for(PackageIDSequence::ConstIteratori(ids->begin()),i_end(ids->end());i!=i_end;++i){if((*i)->version()==id->version()||_same_slot(*i,id))result->push_back(*i);}}returnresult;}boolDecider::_same_slot(conststd::shared_ptr<constPackageID>&a,conststd::shared_ptr<constPackageID>&b)const{if(a->slot_key())returnb->slot_key()&&a->slot_key()->value()==b->slot_key()->value();elsereturn!b->slot_key();}conststd::shared_ptr<Resolution>Decider::_create_resolution_for_resolvent(constResolvent&r)const{returnstd::make_shared<Resolution>(make_named_values<Resolution>(n::constraints()=_initial_constraints_for(r),n::decision()=make_null_shared_ptr(),n::resolvent()=r));}conststd::shared_ptr<Resolution>Decider::_resolution_for_resolvent(constResolvent&r,constTriboolif_not_exist){ResolutionsByResolvent::ConstIteratori(_imp->resolutions_by_resolvent->find(r));if(_imp->resolutions_by_resolvent->end()==i){if(if_not_exist.is_true()){std::shared_ptr<Resolution>resolution(_create_resolution_for_resolvent(r));i=_imp->resolutions_by_resolvent->insert_new(resolution);}elseif(if_not_exist.is_false())throwInternalError(PALUDIS_HERE,"resolver bug: expected resolution for "+stringify(r)+" to exist, but it doesn't");elsereturnmake_null_shared_ptr();}return*i;}conststd::shared_ptr<ConstraintSequence>Decider::_make_constraints_from_target(conststd::shared_ptr<constResolution>&resolution,constPackageOrBlockDepSpec&spec,conststd::shared_ptr<constReason>&reason)const{if(spec.if_package()){autoexisting(_imp->fns.get_use_existing_nothing_fn()(resolution,*spec.if_package(),reason));conststd::shared_ptr<ConstraintSequence>result(std::make_shared<ConstraintSequence>());result->push_back(std::make_shared<Constraint>(make_named_values<Constraint>(n::destination_type()=resolution->resolvent().destination_type(),n::force_unable()=false,n::nothing_is_fine_too()=existing.second,n::reason()=reason,n::spec()=spec,n::untaken()=false,n::use_existing()=existing.first)));returnresult;}elseif(spec.if_block())return_make_constraints_from_blocker(resolution,*spec.if_block(),reason);elsethrowInternalError(PALUDIS_HERE,"resolver bug: huh? it's not a block and it's not a package");}conststd::shared_ptr<ConstraintSequence>Decider::_make_constraints_from_dependency(conststd::shared_ptr<constResolution>&resolution,constSanitisedDependency&dep,conststd::shared_ptr<constReason>&reason,constSpecInterestinterest)const{if(dep.spec().if_package()){autoexisting(_imp->fns.get_use_existing_nothing_fn()(resolution,*dep.spec().if_package(),reason));conststd::shared_ptr<ConstraintSequence>result(std::make_shared<ConstraintSequence>());result->push_back(std::make_shared<Constraint>(make_named_values<Constraint>(n::destination_type()=resolution->resolvent().destination_type(),n::force_unable()=false,n::nothing_is_fine_too()=existing.second,n::reason()=reason,n::spec()=*dep.spec().if_package(),n::untaken()=si_untaken==interest,n::use_existing()=existing.first)));returnresult;}elseif(dep.spec().if_block())return_make_constraints_from_blocker(resolution,*dep.spec().if_block(),reason);elsethrowInternalError(PALUDIS_HERE,"resolver bug: huh? it's not a block and it's not a package");}conststd::shared_ptr<ConstraintSequence>Decider::_make_constraints_from_blocker(conststd::shared_ptr<constResolution>&,constBlockDepSpec&spec,conststd::shared_ptr<constReason>&reason)const{conststd::shared_ptr<ConstraintSequence>result(std::make_shared<ConstraintSequence>());boolnothing_is_fine_too(true),force_unable(false);switch(spec.block_kind()){casebk_weak:casebk_strong:casebk_uninstall_blocked_before:casebk_uninstall_blocked_after:break;casebk_manual:force_unable=!_already_met(spec);break;casebk_upgrade_blocked_before:nothing_is_fine_too=!_already_met(spec);break;caselast_bk:break;}DestinationTypesdestination_types(_get_destination_types_for_blocker(spec,reason));for(EnumIterator<DestinationType>t,t_end(last_dt);t!=t_end;++t)if(destination_types[*t])result->push_back(std::make_shared<Constraint>(make_named_values<Constraint>(n::destination_type()=*t,n::force_unable()=force_unable,n::nothing_is_fine_too()=nothing_is_fine_too,n::reason()=reason,n::spec()=spec,n::untaken()=false,n::use_existing()=ue_if_possible)));returnresult;}voidDecider::_apply_resolution_constraint(conststd::shared_ptr<Resolution>&resolution,conststd::shared_ptr<constConstraint>&constraint){if(resolution->decision())if(!_verify_new_constraint(resolution,constraint))_made_wrong_decision(resolution,constraint);resolution->constraints()->add(constraint);}namespace{structCheckConstraintVisitor{constEnvironment*constenv;conststd::shared_ptr<constChangedChoices>changed_choices_for_constraint;constConstraintconstraint;CheckConstraintVisitor(constEnvironment*conste,conststd::shared_ptr<constChangedChoices>&h,constConstraint&c):env(e),changed_choices_for_constraint(h),constraint(c){}boolok(conststd::shared_ptr<constPackageID>&chosen_id,conststd::shared_ptr<constChangedChoices>&changed_choices)const{if(constraint.force_unable())returnfalse;if(constraint.spec().if_package()){if(!match_package_with_maybe_changes(*env,*constraint.spec().if_package(),changed_choices_for_constraint.get(),*chosen_id,changed_choices.get(),{}))returnfalse;}else{if(match_package_with_maybe_changes(*env,constraint.spec().if_block()->blocking(),changed_choices_for_constraint.get(),*chosen_id,changed_choices.get(),{}))returnfalse;}returntrue;}boolvisit(constChangesToMakeDecision&decision)const{returnok(decision.origin_id(),decision.if_changed_choices()?decision.if_changed_choices()->changed_choices():make_null_shared_ptr());}boolvisit(constExistingNoChangeDecision&decision)const{returnok(decision.existing_id(),make_null_shared_ptr());}boolvisit(constNothingNoChangeDecision&)const{returnconstraint.nothing_is_fine_too();}boolvisit(constUnableToMakeDecision&)const{returntrue;}boolvisit(constRemoveDecision&)const{if(constraint.force_unable())returnfalse;returnconstraint.nothing_is_fine_too();}boolvisit(constBreakDecision&)const{returntrue;}};structCheckUseExistingVisitor{conststd::shared_ptr<constConstraint>constraint;CheckUseExistingVisitor(conststd::shared_ptr<constConstraint>&c):constraint(c){}boolvisit(constExistingNoChangeDecision&decision)const{switch(constraint->use_existing()){caseue_if_possible:break;caseue_only_if_transient:if(!decision.is_transient())returnfalse;break;caseue_if_same:if(!decision.is_same())returnfalse;break;caseue_if_same_version:if(!decision.is_same_version())returnfalse;break;caseue_never:caselast_ue:returnfalse;}returntrue;}boolvisit(constNothingNoChangeDecision&)const{returntrue;}boolvisit(constUnableToMakeDecision&)const{returntrue;}boolvisit(constChangesToMakeDecision&)const{returntrue;}boolvisit(constRemoveDecision&)const{returntrue;}boolvisit(constBreakDecision&)const{returntrue;}};structGetConstraintChangedChoices{conststd::shared_ptr<constChangedChoices>visit(constTargetReason&)const{returnmake_null_shared_ptr();}conststd::shared_ptr<constChangedChoices>visit(constDependentReason&)const{returnmake_null_shared_ptr();}conststd::shared_ptr<constChangedChoices>visit(constPresetReason&)const{returnmake_null_shared_ptr();}conststd::shared_ptr<constChangedChoices>visit(constDependencyReason&r)const{returnr.from_id_changed_choices();}conststd::shared_ptr<constChangedChoices>visit(constViaBinaryReason&)const{returnmake_null_shared_ptr();}conststd::shared_ptr<constChangedChoices>visit(constWasUsedByReason&)const{returnmake_null_shared_ptr();}conststd::shared_ptr<constChangedChoices>visit(constLikeOtherDestinationTypeReason&)const{returnmake_null_shared_ptr();}conststd::shared_ptr<constChangedChoices>visit(constSetReason&r)const{returnr.reason_for_set()->accept_returning<std::shared_ptr<constChangedChoices>>(*this);}};std::shared_ptr<constChangedChoices>get_changed_choices_for(conststd::shared_ptr<constConstraint>&constraint){returnconstraint->reason()->accept_returning<std::shared_ptr<constChangedChoices>>(GetConstraintChangedChoices());}structGetDecisionChangedChoices{conststd::shared_ptr<constChangedChoices>visit(constChangesToMakeDecision&d)const{returnd.if_changed_choices()?d.if_changed_choices()->changed_choices():make_null_shared_ptr();}conststd::shared_ptr<constChangedChoices>visit(constExistingNoChangeDecision&)const{returnmake_null_shared_ptr();}conststd::shared_ptr<constChangedChoices>visit(constNothingNoChangeDecision&)const{returnmake_null_shared_ptr();}conststd::shared_ptr<constChangedChoices>visit(constUnableToMakeDecision&)const{returnmake_null_shared_ptr();}conststd::shared_ptr<constChangedChoices>visit(constRemoveDecision&)const{returnmake_null_shared_ptr();}conststd::shared_ptr<constChangedChoices>visit(constBreakDecision&)const{returnmake_null_shared_ptr();}};std::shared_ptr<constChangedChoices>get_changed_choices_for(conststd::shared_ptr<constDecision>&decision){returndecision->accept_returning<std::shared_ptr<constChangedChoices>>(GetDecisionChangedChoices());}}boolDecider::_check_constraint(conststd::shared_ptr<constConstraint>&constraint,conststd::shared_ptr<constDecision>&decision)const{if(!decision->accept_returning<bool>(CheckConstraintVisitor(_imp->env,get_changed_choices_for(constraint),*constraint)))returnfalse;if(!decision->accept_returning<bool>(CheckUseExistingVisitor(constraint)))returnfalse;if(!constraint->untaken()){if(!decision->taken())returnfalse;}returntrue;}boolDecider::_verify_new_constraint(conststd::shared_ptr<constResolution>&resolution,conststd::shared_ptr<constConstraint>&constraint){return_check_constraint(constraint,resolution->decision());}namespace{structWrongDecisionVisitor{std::function<void()>restart;WrongDecisionVisitor(conststd::function<void()>&r):restart(r){}voidvisit(constNothingNoChangeDecision&)const{/* going from nothing to something is fine */}voidvisit(constRemoveDecision&)const{restart();}voidvisit(constUnableToMakeDecision&)const{restart();}voidvisit(constChangesToMakeDecision&)const{restart();}voidvisit(constExistingNoChangeDecision&)const{restart();}voidvisit(constBreakDecision&)constPALUDIS_ATTRIBUTE((noreturn)){throwInternalError(PALUDIS_HERE,"why are we trying to go from a BreakDecision to something else?");}};}voidDecider::_made_wrong_decision(conststd::shared_ptr<Resolution>&resolution,conststd::shared_ptr<constConstraint>&constraint){/* can we find a resolution that works for all our constraints? */std::shared_ptr<Resolution>adapted_resolution(std::make_shared<Resolution>(*resolution));adapted_resolution->constraints()->add(constraint);conststd::shared_ptr<Decision>decision(_try_to_find_decision_for(adapted_resolution,_allow_choice_changes_for(resolution),false,true,false,true));if(decision){resolution->decision()->accept(WrongDecisionVisitor(std::bind(&Decider::_suggest_restart_with,this,resolution,constraint,decision)));resolution->decision()=decision;}elseresolution->decision()=_cannot_decide_for(adapted_resolution);}voidDecider::_suggest_restart_with(conststd::shared_ptr<constResolution>&resolution,conststd::shared_ptr<constConstraint>&constraint,conststd::shared_ptr<constDecision>&decision)const{throwSuggestRestart(resolution->resolvent(),resolution->decision(),constraint,decision,_make_constraint_for_preloading(decision,constraint));}conststd::shared_ptr<constConstraint>Decider::_make_constraint_for_preloading(conststd::shared_ptr<constDecision>&decision,conststd::shared_ptr<constConstraint>&c)const{conststd::shared_ptr<Constraint>result(std::make_shared<Constraint>(*c));conststd::shared_ptr<PresetReason>reason(std::make_shared<PresetReason>("restarted because of",c->reason()));result->reason()=reason;conststd::shared_ptr<constChangedChoices>changed_choices(get_changed_choices_for(decision));if(result->spec().if_package()){PackageDepSpecs(_make_spec_for_preloading(*result->spec().if_package(),changed_choices));result->spec().if_package()=std::make_shared<PackageDepSpec>(s);}else{PackageDepSpecs(_make_spec_for_preloading(result->spec().if_block()->blocking(),changed_choices));result->spec().if_block()=std::make_shared<BlockDepSpec>("!"+stringify(s),s,result->spec().if_block()->block_kind());}returnresult;}constPackageDepSpecDecider::_make_spec_for_preloading(constPackageDepSpec&spec,conststd::shared_ptr<constChangedChoices>&changed_choices)const{PartiallyMadePackageDepSpecresult(spec);/* we don't want to copy use deps from the constraint, since things like * [foo?] start to get weird when there's no longer an associated ID. */result.clear_additional_requirements();/* but we do want to impose our own ChangedChoices if necessary. */if(changed_choices)changed_choices->add_additional_requirements_to(result);returnresult;}voidDecider::_decide(conststd::shared_ptr<Resolution>&resolution){Contextcontext("When deciding upon an origin ID to use for '"+stringify(resolution->resolvent())+"':");_copy_other_destination_constraints(resolution);std::shared_ptr<Decision>decision(_try_to_find_decision_for(resolution,_allow_choice_changes_for(resolution),false,true,false,true));if(decision)resolution->decision()=decision;elseresolution->decision()=_cannot_decide_for(resolution);}voidDecider::_copy_other_destination_constraints(conststd::shared_ptr<Resolution>&resolution){for(EnumIterator<DestinationType>t,t_end(last_dt);t!=t_end;++t){if(*t==resolution->resolvent().destination_type())continue;Resolventcopy_from_resolvent(resolution->resolvent());copy_from_resolvent.destination_type()=*t;conststd::shared_ptr<Resolution>copy_from_resolution(_resolution_for_resolvent(copy_from_resolvent,indeterminate));if(!copy_from_resolution)continue;for(Constraints::ConstIteratorc(copy_from_resolution->constraints()->begin()),c_end(copy_from_resolution->constraints()->end());c!=c_end;++c){conststd::shared_ptr<ConstraintSequence>constraints(_make_constraints_from_other_destination(resolution,copy_from_resolution,*c));for(ConstraintSequence::ConstIteratord(constraints->begin()),d_end(constraints->end());d!=d_end;++d)_apply_resolution_constraint(resolution,*d);}}}namespace{structDependenciesNecessityVisitor{conststd::pair<std::shared_ptr<constPackageID>,std::shared_ptr<constChangedChoices>>visit(constNothingNoChangeDecision&)const{returnstd::make_pair(make_null_shared_ptr(),make_null_shared_ptr());}conststd::pair<std::shared_ptr<constPackageID>,std::shared_ptr<constChangedChoices>>visit(constRemoveDecision&)const{returnstd::make_pair(make_null_shared_ptr(),make_null_shared_ptr());}conststd::pair<std::shared_ptr<constPackageID>,std::shared_ptr<constChangedChoices>>visit(constUnableToMakeDecision&)const{returnstd::make_pair(make_null_shared_ptr(),make_null_shared_ptr());}conststd::pair<std::shared_ptr<constPackageID>,std::shared_ptr<constChangedChoices>>visit(constExistingNoChangeDecision&decision)const{if(decision.taken())returnstd::make_pair(decision.existing_id(),make_null_shared_ptr());elsereturnstd::make_pair(make_null_shared_ptr(),make_null_shared_ptr());}conststd::pair<std::shared_ptr<constPackageID>,std::shared_ptr<constChangedChoices>>visit(constChangesToMakeDecision&decision)const{if(decision.taken())returnstd::make_pair(decision.origin_id(),decision.if_changed_choices()?decision.if_changed_choices()->changed_choices():make_null_shared_ptr());elsereturnstd::make_pair(make_null_shared_ptr(),make_null_shared_ptr());}conststd::pair<std::shared_ptr<constPackageID>,std::shared_ptr<constChangedChoices>>visit(constBreakDecision&)const{returnstd::make_pair(make_null_shared_ptr(),make_null_shared_ptr());}};}voidDecider::_add_dependencies_if_necessary(conststd::shared_ptr<Resolution>&our_resolution){std::shared_ptr<constPackageID>package_id;std::shared_ptr<constChangedChoices>changed_choices;std::tie(package_id,changed_choices)=our_resolution->decision()->accept_returning<std::pair<std::shared_ptr<constPackageID>,std::shared_ptr<constChangedChoices>>>(DependenciesNecessityVisitor());if(!package_id)return;Contextcontext("When adding dependencies for '"+stringify(our_resolution->resolvent())+"' with '"+stringify(*package_id)+"':");conststd::shared_ptr<SanitisedDependencies>deps(std::make_shared<SanitisedDependencies>());deps->populate(_imp->env,*this,our_resolution,package_id,changed_choices);for(SanitisedDependencies::ConstIterators(deps->begin()),s_end(deps->end());s!=s_end;++s){Contextcontext_2("When handling dependency '"+stringify(s->spec())+"':");SpecInterestinterest(_interest_in_spec(our_resolution,*s));switch(interest){casesi_ignore:continue;casesi_untaken:casesi_take:caselast_si:break;}conststd::shared_ptr<DependencyReason>reason(std::make_shared<DependencyReason>(package_id,changed_choices,our_resolution->resolvent(),*s,_already_met(s->spec())));/* empty resolvents is always ok for blockers, since blocking on things * that don't exist is fine */boolempty_is_ok(s->spec().if_block());std::shared_ptr<constResolvents>resolvents;if(s->spec().if_package())std::tie(resolvents,empty_is_ok)=_get_resolvents_for(*s->spec().if_package(),reason);elseresolvents=_get_resolvents_for_blocker(*s->spec().if_block(),reason);if((!empty_is_ok)&&resolvents->empty())resolvents=_get_error_resolvents_for(*s->spec().if_package(),reason);for(Resolvents::ConstIteratorr(resolvents->begin()),r_end(resolvents->end());r!=r_end;++r){conststd::shared_ptr<Resolution>dep_resolution(_resolution_for_resolvent(*r,true));conststd::shared_ptr<ConstraintSequence>constraints(_make_constraints_from_dependency(our_resolution,*s,reason,interest));for(ConstraintSequence::ConstIteratorc(constraints->begin()),c_end(constraints->end());c!=c_end;++c)_apply_resolution_constraint(dep_resolution,*c);}}}SpecInterestDecider::_interest_in_spec(conststd::shared_ptr<constResolution>&resolution,constSanitisedDependency&dep)const{return_imp->fns.interest_in_spec_fn()(resolution,dep);}conststd::shared_ptr<Constraints>Decider::_initial_constraints_for(constResolvent&r)const{return_imp->fns.get_initial_constraints_for_fn()(r);}std::pair<AnyChildScore,OperatorScore>Decider::find_any_score(conststd::shared_ptr<constResolution>&our_resolution,conststd::shared_ptr<constPackageID>&our_id,constSanitisedDependency&dep)const{Contextcontext("When working out whether we'd like '"+stringify(dep.spec())+"' because of '"+stringify(our_resolution->resolvent())+"':");constboolis_block(dep.spec().if_block());constPackageDepSpec&spec(is_block?dep.spec().if_block()->blocking():*dep.spec().if_package());// note: make sure the worst_score declaration in// AnyDepSpecChildHandler::commit in satitised_dependencies.cc// matches this logicOperatorScoreoperator_bias(os_worse_than_worst);if(spec.version_requirements_ptr()&&!spec.version_requirements_ptr()->empty()){OperatorScorescore(os_worse_than_worst);for(VersionRequirements::ConstIteratorv(spec.version_requirements_ptr()->begin()),v_end(spec.version_requirements_ptr()->end());v!=v_end;++v){OperatorScorelocal_score(os_worse_than_worst);switch(v->version_operator().value()){casevo_greater:casevo_greater_equal:local_score=is_block?os_less:os_greater_or_none;break;casevo_equal:casevo_tilde:casevo_nice_equal_star:casevo_stupid_equal_star:casevo_tilde_greater:local_score=os_equal;break;casevo_less_equal:casevo_less:local_score=is_block?os_greater_or_none:os_less;break;caselast_vo:local_score=os_less;break;}if(score==os_worse_than_worst)score=local_score;elseswitch(spec.version_requirements_mode()){casevr_and:score=is_block?std::max(score,local_score):std::min(score,local_score);break;casevr_or:score=is_block?std::min(score,local_score):std::max(score,local_score);break;caselast_vr:break;}}operator_bias=score;}else{/* don't bias no operator over a >= operator, so || ( >=foo-2 bar ) * still likes foo. */operator_bias=is_block?os_block_everything:os_greater_or_none;}/* explicit preferences come first */if(spec.package_ptr()){Triboolprefer_or_avoid(_imp->fns.prefer_or_avoid_fn()(*spec.package_ptr()));if(prefer_or_avoid.is_true())returnstd::make_pair(is_block?acs_avoid:acs_prefer,operator_bias);elseif(prefer_or_avoid.is_false())returnstd::make_pair(is_block?acs_prefer:acs_avoid,operator_bias);}/* best: blocker that doesn't match anything */if(is_block){conststd::shared_ptr<constPackageIDSequence>ids((*_imp->env)[selection::BestVersionOnly(generator::Matches(spec,{mpo_ignore_additional_requirements})|filter::SupportsAction<InstallAction>()|filter::NotMasked())]);if(ids->empty())returnstd::make_pair(acs_vacuous_blocker,operator_bias);}/* next: already installed */{conststd::shared_ptr<constPackageIDSequence>installed_ids((*_imp->env)[selection::BestVersionOnly(generator::Matches(spec,{})|filter::InstalledAtRoot(_imp->env->system_root_key()->value()))]);if(!installed_ids->empty()^is_block)returnstd::make_pair(acs_already_installed,operator_bias);}/* next: already installed, except with the wrong options */if(!is_block&&spec.additional_requirements_ptr()){conststd::shared_ptr<constPackageIDSequence>installed_ids((*_imp->env)[selection::BestVersionOnly(generator::Matches(spec,{mpo_ignore_additional_requirements})|filter::InstalledAtRoot(_imp->env->system_root_key()->value()))]);if(!installed_ids->empty())returnstd::make_pair(acs_wrong_options_installed,operator_bias);}conststd::shared_ptr<DependencyReason>reason(std::make_shared<DependencyReason>(our_id,make_null_shared_ptr(),our_resolution->resolvent(),dep,_already_met(dep.spec())));conststd::shared_ptr<constResolvents>resolvents(_get_resolvents_for(spec,reason).first);/* next: will already be installing */if(!is_block){for(Resolvents::ConstIteratorr(resolvents->begin()),r_end(resolvents->end());r!=r_end;++r){ResolutionsByResolvent::ConstIteratori(_imp->resolutions_by_resolvent->find(*r));if(i!=_imp->resolutions_by_resolvent->end())returnstd::make_pair(acs_will_be_installing,operator_bias);}}/* next: could install */if(!is_block){for(Resolvents::ConstIteratorr(resolvents->begin()),r_end(resolvents->end());r!=r_end;++r){conststd::shared_ptr<Resolution>resolution(_create_resolution_for_resolvent(*r));conststd::shared_ptr<ConstraintSequence>constraints(_make_constraints_from_dependency(our_resolution,dep,reason,si_take));for(ConstraintSequence::ConstIteratorc(constraints->begin()),c_end(constraints->end());c!=c_end;++c)resolution->constraints()->add(*c);conststd::shared_ptr<Decision>decision(_try_to_find_decision_for(resolution,false,false,false,false,false));if(decision)returnstd::make_pair(acs_could_install,operator_bias);}}/* next: blocks installed package */if(is_block){conststd::shared_ptr<constPackageIDSequence>installed_ids((*_imp->env)[selection::BestVersionOnly(generator::Matches(spec,{})|filter::InstalledAtRoot(_imp->env->system_root_key()->value()))]);if(!installed_ids->empty())returnstd::make_pair(acs_blocks_installed,operator_bias);}/* next: exists */if(!is_block){conststd::shared_ptr<constPackageIDSequence>ids((*_imp->env)[selection::BestVersionOnly(generator::Matches(spec,{mpo_ignore_additional_requirements}))]);if(!ids->empty())returnstd::make_pair(acs_exists,operator_bias);}/* yay, people are depping upon packages that don't exist again. I SMELL A LESSPIPE. */returnstd::make_pair(acs_hate_hate_hate,operator_bias);}namespace{structSlotNameFinder{std::shared_ptr<SlotName>visit(constSlotExactRequirement&s){returnstd::make_shared<SlotName>(s.slot());}std::shared_ptr<SlotName>visit(constSlotAnyUnlockedRequirement&){returnmake_null_shared_ptr();}std::shared_ptr<SlotName>visit(constSlotAnyLockedRequirement&){returnmake_null_shared_ptr();}};}conststd::shared_ptr<constResolvents>Decider::_get_resolvents_for_blocker(constBlockDepSpec&spec,conststd::shared_ptr<constReason>&reason)const{Contextcontext("When finding slots for '"+stringify(spec)+"':");std::shared_ptr<SlotName>exact_slot;if(spec.blocking().slot_requirement_ptr()){SlotNameFinderf;exact_slot=spec.blocking().slot_requirement_ptr()->accept_returning<std::shared_ptr<SlotName>>(f);}DestinationTypesdestination_types(_get_destination_types_for_blocker(spec,reason));std::shared_ptr<Resolvents>result(std::make_shared<Resolvents>());if(exact_slot){for(EnumIterator<DestinationType>t,t_end(last_dt);t!=t_end;++t)if(destination_types[*t])result->push_back(Resolvent(spec.blocking(),*exact_slot,*t));}else{conststd::shared_ptr<constPackageIDSequence>ids((*_imp->env)[selection::BestVersionInEachSlot(generator::Package(*spec.blocking().package_ptr()))]);for(PackageIDSequence::ConstIteratori(ids->begin()),i_end(ids->end());i!=i_end;++i)for(EnumIterator<DestinationType>t,t_end(last_dt);t!=t_end;++t)if(destination_types[*t])result->push_back(Resolvent(*i,*t));}returnresult;}constDestinationTypesDecider::_get_destination_types_for_blocker(constBlockDepSpec&spec,conststd::shared_ptr<constReason>&reason)const{return_imp->fns.get_destination_types_for_blocker_fn()(spec,reason);}conststd::pair<std::shared_ptr<constResolvents>,bool>Decider::_get_resolvents_for(constPackageDepSpec&spec,conststd::shared_ptr<constReason>&reason)const{Contextcontext("When finding slots for '"+stringify(spec)+"':");std::shared_ptr<SlotName>exact_slot;if(spec.slot_requirement_ptr()){SlotNameFinderf;exact_slot=spec.slot_requirement_ptr()->accept_returning<std::shared_ptr<SlotName>>(f);}return_imp->fns.get_resolvents_for_fn()(spec,exact_slot,reason);}constDestinationTypesDecider::_get_destination_types_for_error(constPackageDepSpec&spec,conststd::shared_ptr<constReason>&reason)const{return_imp->fns.get_destination_types_for_error_fn()(spec,reason);}conststd::shared_ptr<constResolvents>Decider::_get_error_resolvents_for(constPackageDepSpec&spec,conststd::shared_ptr<constReason>&reason)const{Contextcontext("When finding slots for '"+stringify(spec)+"', which can't be found the normal way:");std::shared_ptr<Resolvents>result(std::make_shared<Resolvents>());DestinationTypesdestination_types(_get_destination_types_for_error(spec,reason));for(EnumIterator<DestinationType>t,t_end(last_dt);t!=t_end;++t)if(destination_types[*t]){Resolventresolvent(spec,make_named_values<SlotNameOrNull>(n::name_or_null()=make_null_shared_ptr(),n::null_means_unknown()=true),*t);result->push_back(resolvent);}returnresult;}conststd::shared_ptr<Decision>Decider::_try_to_find_decision_for(conststd::shared_ptr<constResolution>&resolution,constboolalso_try_option_changes,constbooltry_option_changes_this_time,constboolalso_try_masked,constbooltry_masked_this_time,constbooltry_removes_if_allowed)const{if(resolution->constraints()->any_force_unable())returnmake_null_shared_ptr();conststd::shared_ptr<constPackageID>existing_id(_find_existing_id_for(resolution));std::shared_ptr<constPackageID>installable_id;std::shared_ptr<constWhyChangedChoices>changed_choices;boolbest;std::tie(installable_id,changed_choices,best)=_find_installable_id_for(resolution,try_option_changes_this_time,try_masked_this_time);if(resolution->constraints()->nothing_is_fine_too()){conststd::shared_ptr<constPackageIDSequence>existing_resolvent_ids(_installed_ids(resolution));if(existing_resolvent_ids->empty()){/* nothing existing, but nothing's ok */returnstd::make_shared<NothingNoChangeDecision>(resolution->resolvent(),!resolution->constraints()->all_untaken());}}if(installable_id&&!existing_id){/* there's nothing suitable existing. */returnstd::make_shared<ChangesToMakeDecision>(resolution->resolvent(),installable_id,changed_choices,best,last_ct,!resolution->constraints()->all_untaken(),make_null_shared_ptr(),std::bind(&Decider::_fixup_changes_to_make_decision,this,resolution,std::placeholders::_1));}elseif(existing_id&&!installable_id){/* there's nothing installable. this may or may not be ok. */boolis_transient(existing_id->behaviours_key()&&existing_id->behaviours_key()->value()->end()!=existing_id->behaviours_key()->value()->find("transient"));switch(resolution->constraints()->strictest_use_existing()){caseue_if_possible:break;caseue_only_if_transient:caseue_if_same:caseue_if_same_version:if(!is_transient)returnmake_null_shared_ptr();break;caseue_never:returnmake_null_shared_ptr();caselast_ue:break;}returnstd::make_shared<ExistingNoChangeDecision>(resolution->resolvent(),existing_id,true,true,is_transient,!resolution->constraints()->all_untaken());}elseif((!existing_id)&&(!installable_id)){/* we can't stick with our existing id, if there is one, and we can't * fix it by installing things. this might be an error, or we might be * able to remove things. */if(try_removes_if_allowed&&resolution->constraints()->nothing_is_fine_too()&&_installed_but_allowed_to_remove(resolution))returnstd::make_shared<RemoveDecision>(resolution->resolvent(),_installed_ids(resolution),!resolution->constraints()->all_untaken());elseif(also_try_option_changes&&!try_option_changes_this_time)return_try_to_find_decision_for(resolution,true,true,also_try_masked,try_masked_this_time,try_removes_if_allowed);elseif(also_try_masked&&!try_masked_this_time)return_try_to_find_decision_for(resolution,also_try_option_changes,try_option_changes_this_time,true,true,try_removes_if_allowed);elsereturnmake_null_shared_ptr();}elseif(existing_id&&installable_id){boolis_same_version(existing_id->version()==installable_id->version());boolis_same(false);if(is_same_version){is_same=true;std::set<ChoiceNameWithPrefix>common;if(existing_id->choices_key()&&installable_id->choices_key()){std::set<ChoiceNameWithPrefix>i_common,u_common;for(Choices::ConstIteratork(installable_id->choices_key()->value()->begin()),k_end(installable_id->choices_key()->value()->end());k!=k_end;++k){if(!(*k)->consider_added_or_changed())continue;for(Choice::ConstIteratori((*k)->begin()),i_end((*k)->end());i!=i_end;++i)if((*i)->explicitly_listed())i_common.insert((*i)->name_with_prefix());}for(Choices::ConstIteratork(existing_id->choices_key()->value()->begin()),k_end(existing_id->choices_key()->value()->end());k!=k_end;++k){if(!(*k)->consider_added_or_changed())continue;for(Choice::ConstIteratori((*k)->begin()),i_end((*k)->end());i!=i_end;++i)if((*i)->explicitly_listed())u_common.insert((*i)->name_with_prefix());}std::set_intersection(i_common.begin(),i_common.end(),u_common.begin(),u_common.end(),std::inserter(common,common.begin()));}for(std::set<ChoiceNameWithPrefix>::const_iteratorf(common.begin()),f_end(common.end());f!=f_end;++f)if(installable_id->choices_key()->value()->find_by_name_with_prefix(*f)->enabled()!=existing_id->choices_key()->value()->find_by_name_with_prefix(*f)->enabled()){is_same=false;break;}}boolis_transient(existing_id->behaviours_key()&&existing_id->behaviours_key()->value()->end()!=existing_id->behaviours_key()->value()->find("transient"));/* we've got existing and installable. do we have any reason not to pick the existing id? */conststd::shared_ptr<Decision>existing(std::make_shared<ExistingNoChangeDecision>(resolution->resolvent(),existing_id,is_same,is_same_version,is_transient,!resolution->constraints()->all_untaken()));conststd::shared_ptr<Decision>changes_to_make(std::make_shared<ChangesToMakeDecision>(resolution->resolvent(),installable_id,changed_choices,best,last_ct,!resolution->constraints()->all_untaken(),make_null_shared_ptr(),std::bind(&Decider::_fixup_changes_to_make_decision,this,resolution,std::placeholders::_1)));switch(resolution->constraints()->strictest_use_existing()){caseue_only_if_transient:caseue_never:returnchanges_to_make;caseue_if_same:if(is_same)returnexisting;elsereturnchanges_to_make;caseue_if_same_version:if(is_same_version)returnexisting;elsereturnchanges_to_make;caseue_if_possible:returnexisting;caselast_ue:break;}}throwInternalError(PALUDIS_HERE,"resolver bug: shouldn't be reached");}conststd::shared_ptr<Decision>Decider::_cannot_decide_for(conststd::shared_ptr<constResolution>&resolution)const{conststd::shared_ptr<UnsuitableCandidates>unsuitable_candidates(std::make_shared<UnsuitableCandidates>());conststd::shared_ptr<constPackageID>existing_id(_find_existing_id_for(resolution));if(existing_id)unsuitable_candidates->push_back(_make_unsuitable_candidate(resolution,existing_id,true));conststd::shared_ptr<constPackageIDSequence>installable_ids(_find_installable_id_candidates_for(resolution,true,false));for(PackageIDSequence::ConstIteratori(installable_ids->begin()),i_end(installable_ids->end());i!=i_end;++i)unsuitable_candidates->push_back(_make_unsuitable_candidate(resolution,*i,false));returnstd::make_shared<UnableToMakeDecision>(resolution->resolvent(),unsuitable_candidates,!resolution->constraints()->all_untaken());}UnsuitableCandidateDecider::_make_unsuitable_candidate(conststd::shared_ptr<constResolution>&resolution,conststd::shared_ptr<constPackageID>&id,constboolexisting)const{returnmake_named_values<UnsuitableCandidate>(n::package_id()=id,n::unmet_constraints()=_get_unmatching_constraints(resolution,id,existing));}conststd::shared_ptr<constPackageID>Decider::_find_existing_id_for(conststd::shared_ptr<constResolution>&resolution)const{conststd::shared_ptr<constPackageIDSequence>ids(_installed_ids(resolution));returnstd::get<0>(_find_id_for_from(resolution,ids,false,false));}boolDecider::_installed_but_allowed_to_remove(conststd::shared_ptr<constResolution>&resolution)const{conststd::shared_ptr<constPackageIDSequence>ids(_installed_ids(resolution));if(ids->empty())returnfalse;returnids->end()==std::find_if(ids->begin(),ids->end(),std::bind(std::logical_not<bool>(),std::bind(&Decider::_allowed_to_remove,this,resolution,std::placeholders::_1)));}boolDecider::_allowed_to_remove(conststd::shared_ptr<constResolution>&resolution,conststd::shared_ptr<constPackageID>&id)const{returnid->supports_action(SupportsActionTest<UninstallAction>())&&_imp->fns.allowed_to_remove_fn()(resolution,id);}boolDecider::_remove_if_dependent(conststd::shared_ptr<constPackageID>&id)const{return_imp->fns.remove_if_dependent_fn()(id);}conststd::shared_ptr<constPackageIDSequence>Decider::_installed_ids(conststd::shared_ptr<constResolution>&resolution)const{return(*_imp->env)[selection::AllVersionsSorted(_make_destination_filtered_generator(generator::Package(resolution->resolvent().package()),resolution)|make_slot_filter(resolution->resolvent()))];}conststd::shared_ptr<constPackageIDSequence>Decider::_find_installable_id_candidates_for(conststd::shared_ptr<constResolution>&resolution,constboolinclude_errors,constboolinclude_unmaskable)const{return(*_imp->env)[selection::AllVersionsSorted(_make_origin_filtered_generator(generator::Package(resolution->resolvent().package()),resolution)|make_slot_filter(resolution->resolvent())|filter::SupportsAction<InstallAction>()|(include_errors?filter::All():include_unmaskable?_make_unmaskable_filter(resolution):filter::NotMasked()))];}constDecider::FoundIDDecider::_find_installable_id_for(conststd::shared_ptr<constResolution>&resolution,constboolinclude_option_changes,constboolinclude_unmaskable)const{return_find_id_for_from(resolution,_find_installable_id_candidates_for(resolution,false,include_unmaskable),include_option_changes,false);}constDecider::FoundIDDecider::_find_id_for_from(conststd::shared_ptr<constResolution>&resolution,conststd::shared_ptr<constPackageIDSequence>&ids,constbooltry_changing_choices,constbooltrying_changing_choices)const{MatchPackageOptionsopts;if(trying_changing_choices)opts+=mpo_ignore_additional_requirements;std::shared_ptr<constPackageID>best_version;for(PackageIDSequence::ReverseConstIteratori(ids->rbegin()),i_end(ids->rend());i!=i_end;++i){if(!best_version)best_version=*i;boolok(true);for(Constraints::ConstIteratorc(resolution->constraints()->begin()),c_end(resolution->constraints()->end());c!=c_end;++c){if((*c)->spec().if_package())ok=ok&&match_package(*_imp->env,*(*c)->spec().if_package(),**i,opts);elseok=ok&&!match_package(*_imp->env,(*c)->spec().if_block()->blocking(),**i,opts);if(!ok)break;}if(ok){autowhy_changed_choices(std::make_shared<WhyChangedChoices>(WhyChangedChoices(make_named_values<WhyChangedChoices>(n::changed_choices()=std::make_shared<ChangedChoices>(),n::reasons()=std::make_shared<Reasons>()))));if(trying_changing_choices){for(Constraints::ConstIteratorc(resolution->constraints()->begin()),c_end(resolution->constraints()->end());c!=c_end;++c){if(!ok)break;if(!(*c)->spec().if_package()){if((*c)->spec().if_block()->blocking().additional_requirements_ptr()&&!(*c)->spec().if_block()->blocking().additional_requirements_ptr()->empty()){/* too complicated for now */ok=false;}break;}if(!(*c)->spec().if_package()->additional_requirements_ptr()){/* no additional requirements, so no tinkering required */continue;}for(autoa((*c)->spec().if_package()->additional_requirements_ptr()->begin()),a_end((*c)->spec().if_package()->additional_requirements_ptr()->end());a!=a_end;++a){autob((*a)->accumulate_changes_to_make_met(_imp->env,get_changed_choices_for(*c).get(),*i,*why_changed_choices->changed_choices()));if(b.is_false()){ok=false;break;}elseif(b.is_true())why_changed_choices->reasons()->push_back((*c)->reason());}}}/* might have an early requirement of [x], and a later [-x], and * chosen to change because of the latter */for(Constraints::ConstIteratorc(resolution->constraints()->begin()),c_end(resolution->constraints()->end());c!=c_end;++c){if(!ok)break;if((*c)->spec().if_package())ok=ok&&match_package_with_maybe_changes(*_imp->env,*(*c)->spec().if_package(),get_changed_choices_for(*c).get(),**i,why_changed_choices->changed_choices().get(),{});elseok=ok&&!match_package_with_maybe_changes(*_imp->env,(*c)->spec().if_block()->blocking(),get_changed_choices_for(*c).get(),**i,why_changed_choices->changed_choices().get(),{});}if(ok)returnFoundID(*i,why_changed_choices->changed_choices()->empty()?make_null_shared_ptr():why_changed_choices,(*i)->version()==best_version->version());}}if(try_changing_choices&&!trying_changing_choices)return_find_id_for_from(resolution,ids,true,true);elsereturnFoundID(make_null_shared_ptr(),make_null_shared_ptr(),false);}conststd::shared_ptr<constConstraints>Decider::_get_unmatching_constraints(conststd::shared_ptr<constResolution>&resolution,conststd::shared_ptr<constPackageID>&id,constboolexisting)const{conststd::shared_ptr<Constraints>result(std::make_shared<Constraints>());for(Constraints::ConstIteratorc(resolution->constraints()->begin()),c_end(resolution->constraints()->end());c!=c_end;++c){std::shared_ptr<Decision>decision;if(existing){boolis_transient(id->behaviours_key()&&id->behaviours_key()->value()->end()!=id->behaviours_key()->value()->find("transient"));decision=std::make_shared<ExistingNoChangeDecision>(resolution->resolvent(),id,true,true,is_transient,!(*c)->untaken());}elsedecision=std::make_shared<ChangesToMakeDecision>(resolution->resolvent(),id,make_null_shared_ptr(),false,last_ct,!(*c)->untaken(),make_null_shared_ptr(),std::function<void(constChangesToMakeDecision&)>());if(!_check_constraint(*c,decision))result->add(*c);}returnresult;}conststd::shared_ptr<constRewrittenSpec>Decider::rewrite_if_special(constPackageOrBlockDepSpec&spec,conststd::shared_ptr<constResolvent>&maybe_from)const{return_imp->rewriter.rewrite_if_special(spec,maybe_from);}voidDecider::add_target_with_reason(constPackageOrBlockDepSpec&spec,conststd::shared_ptr<constReason>&reason){Contextcontext("When adding target '"+stringify(spec)+"':");_imp->env->trigger_notifier_callback(NotifierCallbackResolverStepEvent());conststd::shared_ptr<constRewrittenSpec>if_rewritten(rewrite_if_special(spec,make_null_shared_ptr()));if(if_rewritten){for(Sequence<PackageOrBlockDepSpec>::ConstIteratori(if_rewritten->specs()->begin()),i_end(if_rewritten->specs()->end());i!=i_end;++i)add_target_with_reason(*i,reason);}else{/* empty resolvents is always ok for blockers, since blocking on things * that don't exist is fine */boolempty_is_ok(spec.if_block());std::shared_ptr<constResolvents>resolvents;if(spec.if_package())std::tie(resolvents,empty_is_ok)=_get_resolvents_for(*spec.if_package(),reason);elseresolvents=_get_resolvents_for_blocker(*spec.if_block(),reason);if((!empty_is_ok)&&resolvents->empty())resolvents=_get_error_resolvents_for(*spec.if_package(),reason);for(Resolvents::ConstIteratorr(resolvents->begin()),r_end(resolvents->end());r!=r_end;++r){Contextcontext_2("When adding constraints from target '"+stringify(spec)+"' to resolvent '"+stringify(*r)+"':");conststd::shared_ptr<Resolution>dep_resolution(_resolution_for_resolvent(*r,true));conststd::shared_ptr<ConstraintSequence>constraints(_make_constraints_from_target(dep_resolution,spec,reason));for(ConstraintSequence::ConstIteratorc(constraints->begin()),c_end(constraints->end());c!=c_end;++c)_apply_resolution_constraint(dep_resolution,*c);}}}voidDecider::purge(){Contextcontext("When purging everything:");_imp->env->trigger_notifier_callback(NotifierCallbackResolverStageEvent("Collecting Unused"));conststd::shared_ptr<constPackageIDSet>have_now(collect_installed(_imp->env));conststd::shared_ptr<PackageIDSequence>have_now_seq(std::make_shared<PackageIDSequence>());std::copy(have_now->begin(),have_now->end(),have_now_seq->back_inserter());autounused(collect_purges(_imp->env,have_now,have_now_seq,std::bind(&Environment::trigger_notifier_callback,_imp->env,NotifierCallbackResolverStepEvent())));for(PackageIDSet::ConstIteratori(unused->begin()),i_end(unused->end());i!=i_end;++i){_imp->env->trigger_notifier_callback(NotifierCallbackResolverStepEvent());if(((*i)->behaviours_key()&&(*i)->behaviours_key()->value()->end()!=(*i)->behaviours_key()->value()->find("used"))||(!(*i)->supports_action(SupportsActionTest<UninstallAction>())))continue;Resolventresolvent(*i,dt_install_to_slash);conststd::shared_ptr<Resolution>resolution(_resolution_for_resolvent(resolvent,true));if(resolution->decision())continue;autoused_to_use(std::make_shared<ChangeByResolventSequence>());{autoi_seq(std::make_shared<PackageIDSequence>());i_seq->push_back(*i);for(autou(unused->begin()),u_end(unused->end());u!=u_end;++u)if((*u)->supports_action(SupportsActionTest<UninstallAction>())&&!collect_depped_upon(_imp->env,*u,i_seq,have_now_seq)->empty())used_to_use->push_back(make_named_values<ChangeByResolvent>(n::package_id()=*u,n::resolvent()=Resolvent(*u,dt_install_to_slash)));}conststd::shared_ptr<constConstraintSequence>constraints(_make_constraints_for_purge(resolution,*i,used_to_use));for(ConstraintSequence::ConstIteratorc(constraints->begin()),c_end(constraints->end());c!=c_end;++c)_apply_resolution_constraint(resolution,*c);_decide(resolution);}}voidDecider::resolve(){while(true){_imp->env->trigger_notifier_callback(NotifierCallbackResolverStageEvent("Deciding"));_resolve_decide_with_dependencies();_imp->env->trigger_notifier_callback(NotifierCallbackResolverStageEvent("Vialating"));if(_resolve_vias())continue;_imp->env->trigger_notifier_callback(NotifierCallbackResolverStageEvent("Finding Dependents"));if(_resolve_dependents())continue;_imp->env->trigger_notifier_callback(NotifierCallbackResolverStageEvent("Finding Purgeables"));if(_resolve_purges())continue;break;}_imp->env->trigger_notifier_callback(NotifierCallbackResolverStageEvent("Confirming"));_resolve_confirmations();}boolDecider::_already_met(constPackageOrBlockDepSpec&spec)const{conststd::shared_ptr<constPackageIDSequence>installed_ids((*_imp->env)[selection::AllVersionsUnsorted(generator::Matches(spec.if_package()?*spec.if_package():spec.if_block()->blocking(),{})|filter::InstalledAtRoot(_imp->env->system_root_key()->value()))]);if(installed_ids->empty())returnbool(spec.if_block());else{if(spec.if_block())returnfalse;if(installed_ids->end()==std::find_if(installed_ids->begin(),installed_ids->end(),std::bind(&Decider::_can_use,this,std::placeholders::_1)))returnfalse;returntrue;}}boolDecider::_can_use(conststd::shared_ptr<constPackageID>&id)const{return_imp->fns.can_use_fn()(id);}namespace{structConfirmVisitor{constEnvironment*constenv;constResolverFunctions&fns;conststd::shared_ptr<constResolution>resolution;ConfirmVisitor(constEnvironment*conste,constResolverFunctions&f,conststd::shared_ptr<constResolution>&r):env(e),fns(f),resolution(r){}voidvisit(ChangesToMakeDecision&changes_to_make_decision)const{if(!changes_to_make_decision.best()){conststd::shared_ptr<RequiredConfirmation>c(std::make_shared<NotBestConfirmation>());if(!fns.confirm_fn()(resolution,c))changes_to_make_decision.add_required_confirmation(c);}if(ct_downgrade==changes_to_make_decision.change_type()){conststd::shared_ptr<DowngradeConfirmation>c(std::make_shared<DowngradeConfirmation>());if(!fns.confirm_fn()(resolution,c))changes_to_make_decision.add_required_confirmation(c);}if(changes_to_make_decision.origin_id()->masked()){autoc(std::make_shared<MaskedConfirmation>());if(!fns.confirm_fn()(resolution,c))changes_to_make_decision.add_required_confirmation(c);}if(changes_to_make_decision.if_changed_choices()){autoc(std::make_shared<ChangedChoicesConfirmation>());if(!fns.confirm_fn()(resolution,c))changes_to_make_decision.add_required_confirmation(c);}}voidvisit(BreakDecision&break_decision)const{conststd::shared_ptr<BreakConfirmation>c(std::make_shared<BreakConfirmation>());if(!fns.confirm_fn()(resolution,c))break_decision.add_required_confirmation(c);}voidvisit(UnableToMakeDecision&)const{}voidvisit(ExistingNoChangeDecision&)const{}voidvisit(NothingNoChangeDecision&)const{}voidvisit(RemoveDecision&remove_decision)const{/* we do BreakConfirmation elsewhere */boolis_system(false);for(PackageIDSequence::ConstIteratori(remove_decision.ids()->begin()),i_end(remove_decision.ids()->end());i!=i_end&&!is_system;++i)if(match_package_in_set(*env,*env->set(SetName("system")),**i,{}))is_system=true;if(is_system){conststd::shared_ptr<RemoveSystemPackageConfirmation>c(std::make_shared<RemoveSystemPackageConfirmation>());if(!fns.confirm_fn()(resolution,c))remove_decision.add_required_confirmation(c);}}};}voidDecider::_confirm(conststd::shared_ptr<constResolution>&resolution){resolution->decision()->accept(ConfirmVisitor(_imp->env,_imp->fns,resolution));}boolDecider::_resolve_purges(){Contextcontext("When finding things to purge:");conststd::pair<std::shared_ptr<constChangeByResolventSequence>,std::shared_ptr<constChangeByResolventSequence>>going_away_newly_available(_collect_changing());conststd::shared_ptr<PackageIDSet>going_away(std::make_shared<PackageIDSet>());std::transform(going_away_newly_available.first->begin(),going_away_newly_available.first->end(),going_away->inserter(),get_change_by_resolvent_id);conststd::shared_ptr<PackageIDSet>newly_available(std::make_shared<PackageIDSet>());std::transform(going_away_newly_available.second->begin(),going_away_newly_available.second->end(),newly_available->inserter(),get_change_by_resolvent_id);conststd::shared_ptr<constPackageIDSet>have_now(collect_installed(_imp->env));conststd::shared_ptr<PackageIDSet>have_now_minus_going_away(std::make_shared<PackageIDSet>());std::set_difference(have_now->begin(),have_now->end(),going_away->begin(),going_away->end(),have_now_minus_going_away->inserter(),PackageIDSetComparator());conststd::shared_ptr<PackageIDSet>will_eventually_have_set(std::make_shared<PackageIDSet>());std::copy(have_now_minus_going_away->begin(),have_now_minus_going_away->end(),will_eventually_have_set->inserter());std::copy(newly_available->begin(),newly_available->end(),will_eventually_have_set->inserter());conststd::shared_ptr<PackageIDSequence>will_eventually_have(std::make_shared<PackageIDSequence>());std::copy(will_eventually_have_set->begin(),will_eventually_have_set->end(),will_eventually_have->back_inserter());conststd::shared_ptr<constPackageIDSet>used_originally(accumulate_deps_and_provides(_imp->env,going_away,will_eventually_have,false,std::bind(&Environment::trigger_notifier_callback,_imp->env,NotifierCallbackResolverStepEvent())));conststd::shared_ptr<constPackageIDSet>used_afterwards(accumulate_deps_and_provides(_imp->env,newly_available,will_eventually_have,false,std::bind(&Environment::trigger_notifier_callback,_imp->env,NotifierCallbackResolverStepEvent())));conststd::shared_ptr<PackageIDSet>used_originally_and_not_going_away(std::make_shared<PackageIDSet>());std::set_difference(used_originally->begin(),used_originally->end(),going_away->begin(),going_away->end(),used_originally_and_not_going_away->inserter(),PackageIDSetComparator());conststd::shared_ptr<PackageIDSet>newly_unused(std::make_shared<PackageIDSet>());std::set_difference(used_originally_and_not_going_away->begin(),used_originally_and_not_going_away->end(),used_afterwards->begin(),used_afterwards->end(),newly_unused->inserter(),PackageIDSetComparator());if(newly_unused->empty())returnfalse;conststd::shared_ptr<PackageIDSequence>newly_unused_seq(std::make_shared<PackageIDSequence>());std::copy(newly_unused->begin(),newly_unused->end(),newly_unused_seq->back_inserter());conststd::shared_ptr<PackageIDSequence>have_now_minus_going_away_seq(std::make_shared<PackageIDSequence>());std::copy(have_now_minus_going_away->begin(),have_now_minus_going_away->end(),have_now_minus_going_away_seq->back_inserter());conststd::shared_ptr<PackageIDSet>used_by_unchanging(std::make_shared<PackageIDSet>());for(PackageIDSet::ConstIteratoru(have_now_minus_going_away->begin()),u_end(have_now_minus_going_away->end());u!=u_end;++u){_imp->env->trigger_notifier_callback(NotifierCallbackResolverStepEvent());conststd::shared_ptr<constPackageIDSet>used(collect_depped_upon(_imp->env,*u,newly_unused_seq,have_now_minus_going_away_seq));std::copy(used->begin(),used->end(),used_by_unchanging->inserter());}conststd::shared_ptr<PackageIDSet>newly_really_unused(std::make_shared<PackageIDSet>());std::set_difference(newly_unused->begin(),newly_unused->end(),used_by_unchanging->begin(),used_by_unchanging->end(),newly_really_unused->inserter(),PackageIDSetComparator());conststd::shared_ptr<constSetSpecTree>world(_imp->env->set(SetName("world")));boolchanged(false);for(PackageIDSet::ConstIteratori(newly_really_unused->begin()),i_end(newly_really_unused->end());i!=i_end;++i){_imp->env->trigger_notifier_callback(NotifierCallbackResolverStepEvent());if(((*i)->behaviours_key()&&(*i)->behaviours_key()->value()->end()!=(*i)->behaviours_key()->value()->find("used"))||(!(*i)->supports_action(SupportsActionTest<UninstallAction>())))continue;/* to catch packages being purged that are also in world and not used * by anything else */if(match_package_in_set(*_imp->env,*world,**i,{}))continue;conststd::shared_ptr<ChangeByResolventSequence>used_to_use(std::make_shared<ChangeByResolventSequence>());conststd::shared_ptr<PackageIDSequence>star_i_set(std::make_shared<PackageIDSequence>());star_i_set->push_back(*i);for(ChangeByResolventSequence::ConstIteratorg(going_away_newly_available.first->begin()),g_end(going_away_newly_available.first->end());g!=g_end;++g)if(g->package_id()->supports_action(SupportsActionTest<UninstallAction>())&&!collect_depped_upon(_imp->env,g->package_id(),star_i_set,have_now_minus_going_away_seq)->empty())used_to_use->push_back(*g);Resolventresolvent(*i,dt_install_to_slash);conststd::shared_ptr<Resolution>resolution(_resolution_for_resolvent(resolvent,true));if(resolution->decision())continue;conststd::shared_ptr<constConstraintSequence>constraints(_make_constraints_for_purge(resolution,*i,used_to_use));for(ConstraintSequence::ConstIteratorc(constraints->begin()),c_end(constraints->end());c!=c_end;++c)_apply_resolution_constraint(resolution,*c);_decide(resolution);if(resolution->decision()->taken())changed=true;}returnchanged;}conststd::shared_ptr<ConstraintSequence>Decider::_make_constraints_for_purge(conststd::shared_ptr<constResolution>&resolution,conststd::shared_ptr<constPackageID>&id,conststd::shared_ptr<constChangeByResolventSequence>&r)const{return_imp->fns.get_constraints_for_purge_fn()(resolution,id,r);}namespace{structConstraintFromOtherDestinationVisitor{constDestinationTypedestination_type;conststd::shared_ptr<constConstraint>from_constraint;constResolventresolvent;ConstraintFromOtherDestinationVisitor(constDestinationTypet,conststd::shared_ptr<constConstraint>f,constResolvent&r):destination_type(t),from_constraint(f),resolvent(r){}conststd::shared_ptr<ConstraintSequence>visit(constLikeOtherDestinationTypeReason&)const{std::shared_ptr<ConstraintSequence>result(std::make_shared<ConstraintSequence>());returnresult;}conststd::shared_ptr<ConstraintSequence>visit(constReason&)const{std::shared_ptr<ConstraintSequence>result(std::make_shared<ConstraintSequence>());result->push_back(make_shared_copy(make_named_values<Constraint>(n::destination_type()=destination_type,n::force_unable()=false,n::nothing_is_fine_too()=true,n::reason()=std::make_shared<LikeOtherDestinationTypeReason>(resolvent,from_constraint->reason()),n::spec()=from_constraint->spec(),n::untaken()=from_constraint->untaken(),n::use_existing()=ue_if_possible)));returnresult;}};}conststd::shared_ptr<ConstraintSequence>Decider::_make_constraints_from_other_destination(conststd::shared_ptr<constResolution>&new_resolution,conststd::shared_ptr<constResolution>&from_resolution,conststd::shared_ptr<constConstraint>&from_constraint)const{returnfrom_constraint->reason()->accept_returning<std::shared_ptr<ConstraintSequence>>(ConstraintFromOtherDestinationVisitor(new_resolution->resolvent().destination_type(),from_constraint,from_resolution->resolvent()));}