/* 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.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/resolver_lists.hh>#include<paludis/util/exception.hh>#include<paludis/util/stringify.hh>#include<paludis/util/make_named_values.hh>#include<paludis/util/make_shared_ptr.hh>#include<paludis/util/wrapped_forward_iterator.hh>#include<paludis/util/enum_iterator.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/util/private_implementation_pattern-impl.hh>#include<map>#include<set>usingnamespacepaludis;usingnamespacepaludis::resolver;typedefstd::map<Resolvent,std::tr1::shared_ptr<Resolution>>ResolutionsByResolventMap;namespacepaludis{template<>structImplementation<Decider>{constEnvironment*constenv;constResolverFunctionsfns;SpecRewriterrewriter;ResolutionsByResolventMapresolutions_by_resolvent;conststd::tr1::shared_ptr<ResolverLists>lists;Implementation(constEnvironment*conste,constResolverFunctions&f,conststd::tr1::shared_ptr<ResolverLists>&l):env(e),fns(f),rewriter(env),lists(l){}};}Decider::Decider(constEnvironment*conste,constResolverFunctions&f,conststd::tr1::shared_ptr<ResolverLists>&l):PrivateImplementationPattern<Decider>(newImplementation<Decider>(e,f,l)){}Decider::~Decider(){}voidDecider::_resolve_decide_with_dependencies(){Contextcontext("When resolving and adding dependencies recursively:");enumState{deciding_non_suggestions,deciding_suggestions,finished}state=deciding_non_suggestions;boolchanged(true);while(true){if(!changed)state=State(state+1);if(state==finished)break;changed=false;for(ResolutionsByResolventMap::iteratori(_imp->resolutions_by_resolvent.begin()),i_end(_imp->resolutions_by_resolvent.end());i!=i_end;++i){/* we've already decided */if(i->second->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_non_suggestions&&i->second->constraints()->all_untaken())continue;_imp->env->trigger_notifier_callback(NotifierCallbackResolverStepEvent());changed=true;_decide(i->first,i->second);_add_dependencies_if_necessary(i->first,i->second);}}}voidDecider::_resolve_destinations(){Contextcontext("When resolving destinations:");for(ResolutionsByResolventMap::iteratori(_imp->resolutions_by_resolvent.begin()),i_end(_imp->resolutions_by_resolvent.end());i!=i_end;++i)_do_destination_if_necessary(i->first,i->second);}namespace{structDoDestinationIfNecessaryVisitor{typedefstd::tr1::function<conststd::tr1::shared_ptr<constDestination>(constChangesToMakeDecision&)>MakeDestinationFunc;MakeDestinationFuncmake_destination_for;DoDestinationIfNecessaryVisitor(constMakeDestinationFunc&f):make_destination_for(f){}voidvisit(RemoveDecision&){}voidvisit(ExistingNoChangeDecision&){}voidvisit(NothingNoChangeDecision&){}voidvisit(UnableToMakeDecision&){}voidvisit(ChangesToMakeDecision&decision){if(!decision.destination())decision.set_destination(make_destination_for(decision));}};}voidDecider::_do_destination_if_necessary(constResolvent&resolvent,conststd::tr1::shared_ptr<Resolution>&resolution){DoDestinationIfNecessaryVisitorv(std::tr1::bind(&Decider::_make_destination_for,this,resolvent,resolution,std::tr1::placeholders::_1));resolution->decision()->accept(v);}conststd::tr1::shared_ptr<Destination>Decider::_make_destination_for(constResolvent&resolvent,conststd::tr1::shared_ptr<constResolution>&resolution,constChangesToMakeDecision&decision)const{conststd::tr1::shared_ptr<constRepository>repo(_find_repository_for(resolvent,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()));returnmake_shared_ptr(newDestination(make_named_values<Destination>(value_for<n::replacing>(_find_replacing(decision.origin_id(),repo)),value_for<n::repository>(repo->name()))));}conststd::tr1::shared_ptr<constRepository>Decider::_find_repository_for(constResolvent&resolvent,conststd::tr1::shared_ptr<constResolution>&resolution,constChangesToMakeDecision&decision)const{return_imp->fns.find_repository_for_fn()(resolvent,resolution,decision);}FilteredGeneratorDecider::_make_destination_filtered_generator(constGenerator&g,constResolvent&resolvent)const{return_imp->fns.make_destination_filtered_generator_fn()(g,resolvent);}conststd::tr1::shared_ptr<constPackageIDSequence>Decider::_find_replacing(conststd::tr1::shared_ptr<constPackageID>&id,conststd::tr1::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,RepositoryNameComparator>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::tr1::shared_ptr<PackageIDSequence>result(newPackageIDSequence);for(std::set<RepositoryName,RepositoryNameComparator>::const_iteratorr(repos.begin()),r_end(repos.end());r!=r_end;++r){std::tr1::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::tr1::shared_ptr<constPackageID>&a,conststd::tr1::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::tr1::shared_ptr<Resolution>Decider::_create_resolution_for_resolvent(constResolvent&r)const{returnmake_shared_ptr(newResolution(make_named_values<Resolution>(value_for<n::constraints>(_initial_constraints_for(r)),value_for<n::decision>(make_null_shared_ptr()),value_for<n::resolvent>(r))));}conststd::tr1::shared_ptr<Resolution>Decider::_resolution_for_resolvent(constResolvent&r,constboolcreate){ResolutionsByResolventMap::iteratori(_imp->resolutions_by_resolvent.find(r));if(_imp->resolutions_by_resolvent.end()==i){if(create){std::tr1::shared_ptr<Resolution>resolution(_create_resolution_for_resolvent(r));i=_imp->resolutions_by_resolvent.insert(std::make_pair(r,resolution)).first;_imp->lists->all_resolutions()->append(resolution);}elsethrowInternalError(PALUDIS_HERE,"resolver bug: expected resolution for "+stringify(r)+" to exist, but it doesn't");}returni->second;}conststd::tr1::shared_ptr<Resolution>Decider::resolution_for_resolvent(constResolvent&r)const{ResolutionsByResolventMap::const_iteratori(_imp->resolutions_by_resolvent.find(r));if(_imp->resolutions_by_resolvent.end()==i)throwInternalError(PALUDIS_HERE,"resolver bug: expected resolution for "+stringify(r)+" to exist, but it doesn't");returni->second;}conststd::tr1::shared_ptr<ConstraintSequence>Decider::_make_constraints_from_target(constResolvent&resolvent,constPackageDepSpec&spec,conststd::tr1::shared_ptr<constReason>&reason)const{conststd::tr1::shared_ptr<ConstraintSequence>result(newConstraintSequence);result->push_back(make_shared_ptr(newConstraint(make_named_values<Constraint>(value_for<n::destination_type>(resolvent.destination_type()),value_for<n::nothing_is_fine_too>(false),value_for<n::reason>(reason),value_for<n::spec>(spec),value_for<n::untaken>(false),value_for<n::use_existing>(_imp->fns.get_use_existing_fn()(resolvent,spec,reason))))));returnresult;}conststd::tr1::shared_ptr<ConstraintSequence>Decider::_make_constraints_from_dependency(constResolvent&resolvent,constSanitisedDependency&dep,conststd::tr1::shared_ptr<constReason>&reason)const{conststd::tr1::shared_ptr<ConstraintSequence>result(newConstraintSequence);if(dep.spec().if_package()){result->push_back(make_shared_ptr(newConstraint(make_named_values<Constraint>(value_for<n::destination_type>(resolvent.destination_type()),value_for<n::nothing_is_fine_too>(false),value_for<n::reason>(reason),value_for<n::spec>(*dep.spec().if_package()),value_for<n::untaken>(!_imp->fns.take_dependency_fn()(resolvent,dep,reason)),value_for<n::use_existing>(_imp->fns.get_use_existing_fn()(resolvent,*dep.spec().if_package(),reason))))));}elseif(dep.spec().if_block()){/* nothing is fine too if there's nothing installed matching the block. */conststd::tr1::shared_ptr<constPackageIDSequence>ids((*_imp->env)[selection::SomeArbitraryVersion(generator::Matches(dep.spec().if_block()->blocking(),MatchPackageOptions())|filter::InstalledAtRoot(FSEntry("/")))]);DestinationTypesdestination_types(_get_destination_types_for_blocker(*dep.spec().if_block()));for(EnumIterator<DestinationType>t,t_end(last_dt);t!=t_end;++t)if(destination_types[*t])result->push_back(make_shared_ptr(newConstraint(make_named_values<Constraint>(value_for<n::destination_type>(*t),value_for<n::nothing_is_fine_too>(ids->empty()),value_for<n::reason>(reason),value_for<n::spec>(dep.spec()),value_for<n::untaken>(false),value_for<n::use_existing>(ue_if_possible)))));}elsethrowInternalError(PALUDIS_HERE,"resolver bug: huh? it's not a block and it's not a package");returnresult;}voidDecider::_apply_resolution_constraint(constResolvent&resolvent,conststd::tr1::shared_ptr<Resolution>&resolution,conststd::tr1::shared_ptr<constConstraint>&constraint){if(resolution->decision())if(!_verify_new_constraint(resolvent,resolution,constraint))_made_wrong_decision(resolvent,resolution,constraint);resolution->constraints()->add(constraint);}namespace{structCheckConstraintVisitor{constEnvironment*constenv;constConstraintconstraint;CheckConstraintVisitor(constEnvironment*conste,constConstraint&c):env(e),constraint(c){}boolok(conststd::tr1::shared_ptr<constPackageID>&chosen_id)const{if(constraint.spec().if_package()){if(!match_package(*env,*constraint.spec().if_package(),*chosen_id,MatchPackageOptions()))returnfalse;}else{if(match_package(*env,constraint.spec().if_block()->blocking(),*chosen_id,MatchPackageOptions()))returnfalse;}returntrue;}boolvisit(constChangesToMakeDecision&decision)const{returnok(decision.origin_id());}boolvisit(constExistingNoChangeDecision&decision)const{returnok(decision.existing_id());}boolvisit(constNothingNoChangeDecision&)const{returnconstraint.nothing_is_fine_too();}boolvisit(constUnableToMakeDecision&)const{returnconstraint.nothing_is_fine_too();}boolvisit(constRemoveDecision&)const{returnconstraint.nothing_is_fine_too();}};structCheckUseExistingVisitor{conststd::tr1::shared_ptr<constConstraint>constraint;CheckUseExistingVisitor(conststd::tr1::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;}};}boolDecider::_check_constraint(constResolvent&,conststd::tr1::shared_ptr<constConstraint>&constraint,conststd::tr1::shared_ptr<constDecision>&decision)const{if(!decision->accept_returning<bool>(CheckConstraintVisitor(_imp->env,*constraint)))returnfalse;if(!decision->accept_returning<bool>(CheckUseExistingVisitor(constraint)))returnfalse;if(!constraint->untaken()){if(!decision->taken())returnfalse;}returntrue;}boolDecider::_verify_new_constraint(constResolvent&resolvent,conststd::tr1::shared_ptr<constResolution>&resolution,conststd::tr1::shared_ptr<constConstraint>&constraint){return_check_constraint(resolvent,constraint,resolution->decision());}namespace{structWrongDecisionVisitor{std::tr1::function<void()>restart;WrongDecisionVisitor(conststd::tr1::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();}};}voidDecider::_made_wrong_decision(constResolvent&resolvent,conststd::tr1::shared_ptr<Resolution>&resolution,conststd::tr1::shared_ptr<constConstraint>&constraint){/* can we find a resolution that works for all our constraints? */std::tr1::shared_ptr<Resolution>adapted_resolution(make_shared_ptr(newResolution(*resolution)));adapted_resolution->constraints()->add(constraint);conststd::tr1::shared_ptr<Decision>decision(_try_to_find_decision_for(resolvent,adapted_resolution));if(decision){resolution->decision()->accept(WrongDecisionVisitor(std::tr1::bind(&Decider::_suggest_restart_with,this,resolvent,resolution,constraint,decision)));resolution->decision()=decision;}elseresolution->decision()=_cannot_decide_for(resolvent,adapted_resolution);}voidDecider::_suggest_restart_with(constResolvent&resolvent,conststd::tr1::shared_ptr<constResolution>&resolution,conststd::tr1::shared_ptr<constConstraint>&constraint,conststd::tr1::shared_ptr<constDecision>&decision)const{throwSuggestRestart(resolvent,resolution->decision(),constraint,decision,_make_constraint_for_preloading(resolvent,decision,constraint));}conststd::tr1::shared_ptr<constConstraint>Decider::_make_constraint_for_preloading(constResolvent&,conststd::tr1::shared_ptr<constDecision>&,conststd::tr1::shared_ptr<constConstraint>&c)const{conststd::tr1::shared_ptr<Constraint>result(newConstraint(*c));conststd::tr1::shared_ptr<PresetReason>reason(newPresetReason);result->reason()=reason;if(result->spec().if_package()){PackageDepSpecs(_make_spec_for_preloading(*result->spec().if_package()));result->spec().if_package()=make_shared_ptr(newPackageDepSpec(s));}else{PackageDepSpecs(_make_spec_for_preloading(result->spec().if_block()->blocking()));result->spec().if_block()=make_shared_ptr(newBlockDepSpec("!"+stringify(s),s,result->spec().if_block()->strong()));}returnresult;}constPackageDepSpecDecider::_make_spec_for_preloading(constPackageDepSpec&spec)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();returnresult;}voidDecider::_decide(constResolvent&resolvent,conststd::tr1::shared_ptr<Resolution>&resolution){Contextcontext("When deciding upon an origin ID to use for '"+stringify(resolvent)+"':");std::tr1::shared_ptr<Decision>decision(_try_to_find_decision_for(resolvent,resolution));if(decision)resolution->decision()=decision;elseresolution->decision()=_cannot_decide_for(resolvent,resolution);}namespace{structDependenciesNecessityVisitor{conststd::tr1::shared_ptr<constPackageID>visit(constNothingNoChangeDecision&)const{returnmake_null_shared_ptr();}conststd::tr1::shared_ptr<constPackageID>visit(constRemoveDecision&)const{returnmake_null_shared_ptr();}conststd::tr1::shared_ptr<constPackageID>visit(constUnableToMakeDecision&)const{returnmake_null_shared_ptr();}conststd::tr1::shared_ptr<constPackageID>visit(constExistingNoChangeDecision&decision)const{if(decision.taken())returndecision.existing_id();elsereturnmake_null_shared_ptr();}conststd::tr1::shared_ptr<constPackageID>visit(constChangesToMakeDecision&decision)const{if(decision.taken())returndecision.origin_id();elsereturnmake_null_shared_ptr();}};}voidDecider::_add_dependencies_if_necessary(constResolvent&our_resolvent,conststd::tr1::shared_ptr<Resolution>&our_resolution){conststd::tr1::shared_ptr<constPackageID>package_id(our_resolution->decision()->accept_returning<std::tr1::shared_ptr<constPackageID>>(DependenciesNecessityVisitor()));if(!package_id)return;Contextcontext("When adding dependencies for '"+stringify(our_resolvent)+"' with '"+stringify(*package_id)+"':");conststd::tr1::shared_ptr<SanitisedDependencies>deps(newSanitisedDependencies);deps->populate(*this,our_resolvent,package_id);for(SanitisedDependencies::ConstIterators(deps->begin()),s_end(deps->end());s!=s_end;++s){Contextcontext_2("When handling dependency '"+stringify(s->spec())+"':");if(!_care_about_dependency_spec(our_resolvent,our_resolution,*s))continue;conststd::tr1::shared_ptr<DependencyReason>reason(newDependencyReason(package_id,our_resolvent,*s,_already_met(*s)));std::tr1::shared_ptr<constResolvents>resolvents;if(s->spec().if_package())resolvents=_get_resolvents_for(*s->spec().if_package(),reason);elseresolvents=_get_resolvents_for_blocker(*s->spec().if_block());if(resolvents->empty()){if(s->spec().if_package())resolvents=_get_error_resolvents_for(*s->spec().if_package(),reason);else{/* blocking on something that doesn't exist is fine */}}for(Resolvents::ConstIteratorr(resolvents->begin()),r_end(resolvents->end());r!=r_end;++r){conststd::tr1::shared_ptr<Resolution>dep_resolution(_resolution_for_resolvent(*r,true));conststd::tr1::shared_ptr<ConstraintSequence>constraints(_make_constraints_from_dependency(our_resolvent,*s,reason));for(ConstraintSequence::ConstIteratorc(constraints->begin()),c_end(constraints->end());c!=c_end;++c)_apply_resolution_constraint(*r,dep_resolution,*c);}}}boolDecider::_care_about_dependency_spec(constResolvent&resolvent,conststd::tr1::shared_ptr<constResolution>&resolution,constSanitisedDependency&dep)const{return_imp->fns.care_about_dep_fn()(resolvent,resolution,dep);}conststd::tr1::shared_ptr<Constraints>Decider::_initial_constraints_for(constResolvent&r)const{return_imp->fns.get_initial_constraints_for_fn()(r);}namespace{structChosenIDVisitor{conststd::tr1::shared_ptr<constPackageID>visit(constChangesToMakeDecision&decision)const{returndecision.origin_id();}conststd::tr1::shared_ptr<constPackageID>visit(constExistingNoChangeDecision&decision)const{returndecision.existing_id();}conststd::tr1::shared_ptr<constPackageID>visit(constNothingNoChangeDecision&)const{returnmake_null_shared_ptr();}conststd::tr1::shared_ptr<constPackageID>visit(constUnableToMakeDecision&)const{returnmake_null_shared_ptr();}conststd::tr1::shared_ptr<constPackageID>visit(constRemoveDecision&)const{returnmake_null_shared_ptr();}};}std::pair<AnyChildScore,OperatorScore>Decider::find_any_score(constResolvent&our_resolvent,constSanitisedDependency&dep)const{Contextcontext("When working out whether we'd like '"+stringify(dep.spec())+"' because of '"+stringify(our_resolvent)+"':");if(dep.spec().if_block())throwInternalError(PALUDIS_HERE,"unimplemented: blockers inside || blocks are horrid");constPackageDepSpec&spec(*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=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=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=std::min(score,local_score);break;casevr_or: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=os_greater_or_none;}/* best: already installed */{conststd::tr1::shared_ptr<constPackageIDSequence>installed_ids((*_imp->env)[selection::BestVersionOnly(generator::Matches(spec,MatchPackageOptions())|filter::InstalledAtRoot(FSEntry("/")))]);if(!installed_ids->empty())returnstd::make_pair(acs_already_installed,operator_bias);}/* next: already installed, except with the wrong options */if(spec.additional_requirements_ptr()){conststd::tr1::shared_ptr<constPackageIDSequence>installed_ids((*_imp->env)[selection::BestVersionOnly(generator::Matches(spec,MatchPackageOptions()+mpo_ignore_additional_requirements)|filter::InstalledAtRoot(FSEntry("/")))]);if(!installed_ids->empty())returnstd::make_pair(acs_wrong_options_installed,operator_bias);}conststd::tr1::shared_ptr<constPackageID>id(resolution_for_resolvent(our_resolvent)->decision()->accept_returning<std::tr1::shared_ptr<constPackageID>>(ChosenIDVisitor()));if(!id)throwInternalError(PALUDIS_HERE,"resolver bug: why don't we have an id?");conststd::tr1::shared_ptr<DependencyReason>reason(newDependencyReason(id,our_resolvent,dep,_already_met(dep)));conststd::tr1::shared_ptr<constResolvents>resolvents(_get_resolvents_for(spec,reason));/* next: will already be installing */{for(Resolvents::ConstIteratorr(resolvents->begin()),r_end(resolvents->end());r!=r_end;++r){ResolutionsByResolventMap::const_iteratori(_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 */{for(Resolvents::ConstIteratorr(resolvents->begin()),r_end(resolvents->end());r!=r_end;++r){conststd::tr1::shared_ptr<Resolution>resolution(_create_resolution_for_resolvent(*r));conststd::tr1::shared_ptr<ConstraintSequence>constraints(_make_constraints_from_dependency(our_resolvent,dep,reason));for(ConstraintSequence::ConstIteratorc(constraints->begin()),c_end(constraints->end());c!=c_end;++c)resolution->constraints()->add(*c);conststd::tr1::shared_ptr<Decision>decision(_try_to_find_decision_for(*r,resolution));if(decision)returnstd::make_pair(acs_could_install,operator_bias);}}/* next: exists */{conststd::tr1::shared_ptr<constPackageIDSequence>ids((*_imp->env)[selection::BestVersionOnly(generator::Matches(spec,MatchPackageOptions()+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::tr1::shared_ptr<SlotName>visit(constSlotExactRequirement&s){returnmake_shared_ptr(newSlotName(s.slot()));}std::tr1::shared_ptr<SlotName>visit(constSlotAnyUnlockedRequirement&){returnmake_null_shared_ptr();}std::tr1::shared_ptr<SlotName>visit(constSlotAnyLockedRequirement&){returnmake_null_shared_ptr();}};}conststd::tr1::shared_ptr<constResolvents>Decider::_get_resolvents_for_blocker(constBlockDepSpec&spec)const{Contextcontext("When finding slots for '"+stringify(spec)+"':");std::tr1::shared_ptr<SlotName>exact_slot;if(spec.blocking().slot_requirement_ptr()){SlotNameFinderf;exact_slot=spec.blocking().slot_requirement_ptr()->accept_returning<std::tr1::shared_ptr<SlotName>>(f);}DestinationTypesdestination_types(_get_destination_types_for_blocker(spec));std::tr1::shared_ptr<Resolvents>result(newResolvents);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::tr1::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&)const{returnDestinationTypes()+dt_install_to_slash;}conststd::tr1::shared_ptr<constResolvents>Decider::_get_resolvents_for(constPackageDepSpec&spec,conststd::tr1::shared_ptr<constReason>&reason)const{Contextcontext("When finding slots for '"+stringify(spec)+"':");std::tr1::shared_ptr<SlotName>exact_slot;if(spec.slot_requirement_ptr()){SlotNameFinderf;exact_slot=spec.slot_requirement_ptr()->accept_returning<std::tr1::shared_ptr<SlotName>>(f);}return_imp->fns.get_resolvents_for_fn()(spec,exact_slot,reason);}constDestinationTypesDecider::_get_destination_types_for(constPackageDepSpec&spec,conststd::tr1::shared_ptr<constPackageID>&id,conststd::tr1::shared_ptr<constReason>&reason)const{return_imp->fns.get_destination_types_for_fn()(spec,id,reason);}conststd::tr1::shared_ptr<constResolvents>Decider::_get_error_resolvents_for(constPackageDepSpec&spec,conststd::tr1::shared_ptr<constReason>&reason)const{Contextcontext("When finding slots for '"+stringify(spec)+"', which can't be found the normal way:");std::tr1::shared_ptr<Resolvents>result(newResolvents);DestinationTypesdestination_types(_get_destination_types_for(spec,make_null_shared_ptr(),reason));for(EnumIterator<DestinationType>t,t_end(last_dt);t!=t_end;++t)if(destination_types[*t])result->push_back(Resolvent(spec,true,*t));returnresult;}conststd::tr1::shared_ptr<Decision>Decider::_try_to_find_decision_for(constResolvent&resolvent,conststd::tr1::shared_ptr<constResolution>&resolution)const{conststd::tr1::shared_ptr<constPackageID>existing_id(_find_existing_id_for(resolvent,resolution));std::pair<conststd::tr1::shared_ptr<constPackageID>,bool>installable_id_best(_find_installable_id_for(resolvent,resolution));conststd::tr1::shared_ptr<constPackageID>installable_id(installable_id_best.first);boolbest(installable_id_best.second);if(resolution->constraints()->nothing_is_fine_too()&&!existing_id){/* nothing existing, but nothing's ok */returnmake_shared_ptr(newNothingNoChangeDecision(!resolution->constraints()->all_untaken()));}elseif(installable_id&&!existing_id){/* there's nothing suitable existing. */returnmake_shared_ptr(newChangesToMakeDecision(installable_id,best,!resolution->constraints()->all_untaken(),make_null_shared_ptr()));}elseif(existing_id&&!installable_id){/* there's nothing installable. this may or may not be ok. */boolis_transient(existing_id->transient_key()&&existing_id->transient_key()->value());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;}returnmake_shared_ptr(newExistingNoChangeDecision(existing_id,true,true,is_transient,!resolution->constraints()->all_untaken()));}elseif((!existing_id)&&(!installable_id)){returnmake_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->transient_key()&&existing_id->transient_key()->value());/* we've got existing and installable. do we have any reason not to pick the existing id? */conststd::tr1::shared_ptr<Decision>existing(newExistingNoChangeDecision(existing_id,is_same,is_same_version,is_transient,!resolution->constraints()->all_untaken()));conststd::tr1::shared_ptr<Decision>changes_to_make(newChangesToMakeDecision(installable_id,best,!resolution->constraints()->all_untaken(),make_null_shared_ptr()));switch(resolution->constraints()->strictest_use_existing()){caseue_only_if_transient:caseue_never:returnmake_shared_ptr(newChangesToMakeDecision(installable_id,best,!resolution->constraints()->all_untaken(),make_null_shared_ptr()));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::tr1::shared_ptr<Decision>Decider::_cannot_decide_for(constResolvent&resolvent,conststd::tr1::shared_ptr<constResolution>&resolution)const{conststd::tr1::shared_ptr<UnsuitableCandidates>unsuitable_candidates(newUnsuitableCandidates);conststd::tr1::shared_ptr<constPackageID>existing_id(_find_existing_id_for(resolvent,resolution));if(existing_id)unsuitable_candidates->push_back(_make_unsuitable_candidate(resolvent,resolution,existing_id));conststd::tr1::shared_ptr<constPackageIDSequence>installable_ids(_find_installable_id_candidates_for(resolvent,resolution,true));for(PackageIDSequence::ConstIteratori(installable_ids->begin()),i_end(installable_ids->end());i!=i_end;++i)unsuitable_candidates->push_back(_make_unsuitable_candidate(resolvent,resolution,*i));returnmake_shared_ptr(newUnableToMakeDecision(unsuitable_candidates,!resolution->constraints()->all_untaken()));}UnsuitableCandidateDecider::_make_unsuitable_candidate(constResolvent&resolvent,conststd::tr1::shared_ptr<constResolution>&,conststd::tr1::shared_ptr<constPackageID>&id)const{returnmake_named_values<UnsuitableCandidate>(value_for<n::package_id>(id),value_for<n::unmet_constraints>(_get_unmatching_constraints(resolvent,id)));}conststd::tr1::shared_ptr<constPackageID>Decider::_find_existing_id_for(constResolvent&resolvent,conststd::tr1::shared_ptr<constResolution>&resolution)const{conststd::tr1::shared_ptr<constPackageIDSequence>ids((*_imp->env)[selection::AllVersionsSorted(_make_destination_filtered_generator(generator::Package(resolvent.package()),resolvent)|make_slot_filter(resolvent))]);return_find_id_for_from(resolvent,resolution,ids).first;}conststd::tr1::shared_ptr<constPackageIDSequence>Decider::_find_installable_id_candidates_for(constResolvent&resolvent,conststd::tr1::shared_ptr<constResolution>&,constboolinclude_errors)const{return(*_imp->env)[selection::AllVersionsSorted(generator::Package(resolvent.package())|make_slot_filter(resolvent)|filter::SupportsAction<InstallAction>()|((!include_errors)?Filter(filter::NotMasked()):Filter(filter::All())))];}conststd::pair<conststd::tr1::shared_ptr<constPackageID>,bool>Decider::_find_installable_id_for(constResolvent&resolvent,conststd::tr1::shared_ptr<constResolution>&resolution)const{return_find_id_for_from(resolvent,resolution,_find_installable_id_candidates_for(resolvent,resolution,false));}conststd::pair<conststd::tr1::shared_ptr<constPackageID>,bool>Decider::_find_id_for_from(constResolvent&,conststd::tr1::shared_ptr<constResolution>&resolution,conststd::tr1::shared_ptr<constPackageIDSequence>&ids)const{boolbest(true);for(PackageIDSequence::ReverseConstIteratori(ids->rbegin()),i_end(ids->rend());i!=i_end;++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,MatchPackageOptions());elseok=ok&&!match_package(*_imp->env,(*c)->spec().if_block()->blocking(),**i,MatchPackageOptions());if(!ok)break;}if(ok)returnstd::make_pair(*i,best);best=false;}returnstd::make_pair(make_null_shared_ptr(),false);}conststd::tr1::shared_ptr<constConstraints>Decider::_get_unmatching_constraints(constResolvent&resolvent,conststd::tr1::shared_ptr<constPackageID>&id)const{conststd::tr1::shared_ptr<constResolution>resolution(resolution_for_resolvent(resolvent));conststd::tr1::shared_ptr<Constraints>result(newConstraints);for(Constraints::ConstIteratorc(resolution->constraints()->begin()),c_end(resolution->constraints()->end());c!=c_end;++c){if(!_check_constraint(resolvent,*c,make_shared_ptr(newChangesToMakeDecision(id,false,!(*c)->untaken(),make_null_shared_ptr()))))result->add(*c);}returnresult;}conststd::tr1::shared_ptr<constRewrittenSpec>Decider::rewrite_if_special(constPackageOrBlockDepSpec&spec,conststd::tr1::shared_ptr<constResolvent>&maybe_from)const{return_imp->rewriter.rewrite_if_special(spec,maybe_from);}voidDecider::add_target_with_reason(constPackageDepSpec&spec,conststd::tr1::shared_ptr<constReason>&reason){Contextcontext("When adding target '"+stringify(spec)+"':");_imp->env->trigger_notifier_callback(NotifierCallbackResolverStepEvent());conststd::tr1::shared_ptr<constRewrittenSpec>if_rewritten(rewrite_if_special(PackageOrBlockDepSpec(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)if(i->if_package())add_target_with_reason(*i->if_package(),reason);elsethrowInternalError(PALUDIS_HERE,"resolver bug: rewritten "+stringify(spec)+" includes "+stringify(*i));}else{std::tr1::shared_ptr<constResolvents>resolvents(_get_resolvents_for(spec,reason));if(resolvents->empty())resolvents=_get_error_resolvents_for(spec,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::tr1::shared_ptr<Resolution>dep_resolution(_resolution_for_resolvent(*r,true));conststd::tr1::shared_ptr<ConstraintSequence>constraints(_make_constraints_from_target(*r,spec,reason));for(ConstraintSequence::ConstIteratorc(constraints->begin()),c_end(constraints->end());c!=c_end;++c)_apply_resolution_constraint(*r,dep_resolution,*c);}}}voidDecider::resolve(){_imp->env->trigger_notifier_callback(NotifierCallbackResolverStageEvent("Deciding"));_resolve_decide_with_dependencies();_imp->env->trigger_notifier_callback(NotifierCallbackResolverStageEvent("Finding Destinations"));_resolve_destinations();}boolDecider::_already_met(constSanitisedDependency&dep)const{conststd::tr1::shared_ptr<constPackageIDSequence>installed_ids((*_imp->env)[selection::BestVersionOnly(generator::Matches(dep.spec().if_package()?*dep.spec().if_package():dep.spec().if_block()->blocking(),MatchPackageOptions())|filter::InstalledAtRoot(FSEntry("/")))]);if(installed_ids->empty())returndep.spec().if_block();elsereturndep.spec().if_package();}