From f1a068105d16d3b1a4bb4cdda5205b6b7d4df009 Mon Sep 17 00:00:00 2001 From: Klaus Kaempf Date: Tue, 31 Jan 2006 19:26:16 +0000 Subject: [PATCH] port 'doUpgrade' and make it available at toplevel --- zypp/Resolver.cc | 8 +- zypp/Resolver.h | 20 +- zypp/solver/detail/Resolver.cc | 71 ++-- zypp/solver/detail/Resolver.h | 14 +- zypp/solver/detail/ResolverUpgrade.cc | 597 ++++++++++++++++++++++++++++++++++ 5 files changed, 681 insertions(+), 29 deletions(-) create mode 100644 zypp/solver/detail/ResolverUpgrade.cc diff --git a/zypp/Resolver.cc b/zypp/Resolver.cc index 19dc77f..9a25aa3 100644 --- a/zypp/Resolver.cc +++ b/zypp/Resolver.cc @@ -12,6 +12,7 @@ #include #include "zypp/Resolver.h" +#include "zypp/UpgradeStatistics.h" #include "zypp/solver/detail/Resolver.h" using namespace std; @@ -57,15 +58,16 @@ namespace zypp void Resolver::verifySystem () { return _pimpl->verifySystem(); } - void Resolver::establishState () - { return _pimpl->establishState(); } + void Resolver::establishPool () + { return _pimpl->establishPool(); } bool Resolver::resolvePool () { return _pimpl->resolvePool (); } ResolverProblemList Resolver::problems () { return _pimpl->problems (); } bool Resolver::applySolutions( const ProblemSolutionList & solutions ) { return _pimpl->applySolutions (solutions); } - + void Resolver::doUpgrade( UpgradeStatistics & opt_stats_r ) + { return _pimpl->doUpgrade(opt_stats_r); } // ResolverContext_constPtr bestContext (void) const; diff --git a/zypp/Resolver.h b/zypp/Resolver.h index 57df2f4..3cee509 100644 --- a/zypp/Resolver.h +++ b/zypp/Resolver.h @@ -19,6 +19,7 @@ #include "zypp/base/PtrTypes.h" #include "zypp/ResPool.h" +#include "zypp/UpgradeStatistics.h" #include "zypp/solver/detail/Resolver.h" #include "zypp/solver/detail/ResolverContext.h" #include "zypp/ProblemTypes.h" @@ -53,13 +54,13 @@ namespace zypp void verifySystem (void); /** - * Establish state of 'higher level' Resolvables + * Establish state of 'higher level' Resolvables in Pool * * Must be called when dealing with non-package resolvables, * like Patches, Patterns, and Products * **/ - void establishState (void); + void establishPool (void); /** * Resolve package dependencies: @@ -75,6 +76,21 @@ namespace zypp bool resolvePool (void); /** + * Do an distribution upgrade + * + * This will run a full upgrade on the pool, taking all upgrade + * dependencies (provide/obsolete for package renames, split- + * provides, etc.) into account and actually removing installed + * packages if no upgrade exists. + * + * To be run with great caution. It basically brings your + * system 'back to start'. + * Quite helpful to get back to a 'sane state'. Quite disastrous + * since you'll loose all non-distribution packages + **/ + void doUpgrade( UpgradeStatistics & opt_stats_r ); + + /** * Return the dependency problems found by the last call to * resolveDependencies(). If there were no problems, the returned * list will be empty. diff --git a/zypp/solver/detail/Resolver.cc b/zypp/solver/detail/Resolver.cc index 3a8c603..21637ee 100644 --- a/zypp/solver/detail/Resolver.cc +++ b/zypp/solver/detail/Resolver.cc @@ -47,8 +47,8 @@ IMPL_PTR_TYPE(Resolver); //--------------------------------------------------------------------------- -ostream& -operator<<( ostream& os, const Resolver & resolver) +std::ostream & +Resolver::dumpOn( std::ostream & os ) const { return os << ""; } @@ -243,6 +243,34 @@ Resolver::verifySystem (void) //--------------------------------------------------------------------------- +// copy marked item from solution back to pool + +static void +solution_to_pool (PoolItem_Ref item, const ResStatus & status, void *data) +{ + if (status.isToBeInstalled()) { + item.status().setToBeInstalled(ResStatus::SOLVER); + } + else if (status.isToBeUninstalled()) { + item.status().setToBeUninstalled(ResStatus::SOLVER); + } + else if (status.isIncomplete() + || status.isNeeded()) { + item.status().setIncomplete(); + } + else if (status.isUnneeded()) { + item.status().setUnneeded(); + } + else if (status.isSatisfied()) { + item.status().setSatisfied(); + } + return; +} + + +//--------------------------------------------------------------------------- + + // establish state struct EstablishState : public resfilter::OnCapMatchCallbackFunctor @@ -300,6 +328,23 @@ Resolver::establishState (ResolverContext_Ptr context) return; } + +void +Resolver::establishPool () +{ + establishState (); // establish ! + ResolverContext_Ptr solution = bestContext(); + + if (solution) { // copy solution back to pool + solution->foreachMarked (solution_to_pool, NULL); + } + else { + ERR << "establishState did not return a bestContext" << endl; + } + + return; +} + //--------------------------------------------------------------------------- bool @@ -507,7 +552,7 @@ struct CollectTransact : public resfilter::PoolItemFilterFunctor { ResStatus status = item.status(); if (status.isBySolver()) { // clear any solver transactions - item.status().setNoTransact (ResStatus::SOLVER); + item.status().setNoTransact(ResStatus::SOLVER); } if (status.isUninstalled()) { // transact && uninstalled resolver.addPoolItemToInstall(item); // -> install! @@ -520,26 +565,6 @@ struct CollectTransact : public resfilter::PoolItemFilterFunctor }; -// copy marked item from solution back to pool - -static void -solution_to_pool (PoolItem_Ref item, const ResStatus & status, void *data) -{ - if (status.isToBeInstalled()) { - item.status().setToBeInstalled (ResStatus::SOLVER); - } - else if (status.isToBeUninstalled()) { - item.status().setToBeUninstalled (ResStatus::SOLVER); - } - else if (status.isIncomplete() - || status.isNeeded()) { - item.status().setIncomplete(); - } - return; -} - - - // This function loops over the pool and grabs // all item.status().transacts() and item.status().byUser() // It clears all previous bySolver() states also diff --git a/zypp/solver/detail/Resolver.h b/zypp/solver/detail/Resolver.h index a1cea70..c37b3c2 100644 --- a/zypp/solver/detail/Resolver.h +++ b/zypp/solver/detail/Resolver.h @@ -34,12 +34,15 @@ #include "zypp/solver/detail/Types.h" #include "zypp/solver/detail/ResolverQueue.h" #include "zypp/solver/detail/ResolverContext.h" + #include "zypp/ProblemTypes.h" #include "zypp/ResolverProblem.h" #include "zypp/ProblemSolution.h" +#include "zypp/UpgradeStatistics.h" #include "zypp/CapSet.h" + ///////////////////////////////////////////////////////////////////////// namespace zypp { /////////////////////////////////////////////////////////////////////// @@ -84,6 +87,10 @@ class Resolver : public base::ReferenceCounted, private base::NonCopyable { std::set _subscribed; + // helpers + bool doesObsoleteCapability (PoolItem_Ref candidate, const Capability & cap); + bool doesObsoleteItem (PoolItem_Ref candidate, PoolItem_Ref installed); + public: Resolver (const ResPool & pool); @@ -91,7 +98,9 @@ class Resolver : public base::ReferenceCounted, private base::NonCopyable { // ---------------------------------- I/O - friend std::ostream& operator<<(std::ostream&, const Resolver &resolver); + virtual std::ostream & dumpOn( std::ostream & str ) const; + friend std::ostream& operator<<(std::ostream& str, const Resolver & obj) + { return obj.dumpOn (str); } // ---------------------------------- accessors @@ -130,9 +139,12 @@ class Resolver : public base::ReferenceCounted, private base::NonCopyable { void verifySystem (void); void establishState (const ResolverContext_Ptr context = NULL); + void establishPool (void); bool resolveDependencies (const ResolverContext_Ptr context = NULL); bool resolvePool (void); + void doUpgrade( zypp::UpgradeStatistics & opt_stats_r ); + ResolverProblemList problems (void) const; bool applySolutions (const ProblemSolutionList &solutions); diff --git a/zypp/solver/detail/ResolverUpgrade.cc b/zypp/solver/detail/ResolverUpgrade.cc new file mode 100644 index 0000000..961c184 --- /dev/null +++ b/zypp/solver/detail/ResolverUpgrade.cc @@ -0,0 +1,597 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/*---------------------------------------------------------------------\ +| ____ _ __ __ ___ | +| |__ / \ / / . \ . \ | +| / / \ V /| _/ _/ | +| / /__ | | | | | | | +| /_____||_| |_| |_| | +| | +\---------------------------------------------------------------------*/ +/* ResolverUpgrade.cc + * + * Implements the distribution upgrade algorithm. + * + * Copyright (C) 2005 SUSE Linux Products GmbH + * + * This program 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. + * + * This program 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. + */ + +/* + stolen from PMPackageManager_update.cc + original author Michael Andres + zypp port by Klaus Kaempf + +/-*/ + +#include "zypp/CapSet.h" +#include "zypp/capability/SplitCap.h" + +#include "zypp/base/Logger.h" +#include "zypp/base/String.h" +#include "zypp/base/Gettext.h" + +#include "zypp/base/Algorithm.h" +#include "zypp/ResPool.h" +#include "zypp/ResStatus.h" +#include "zypp/ResFilters.h" +#include "zypp/CapFilters.h" +#include "zypp/Capability.h" +#include "zypp/CapFactory.h" +#include "zypp/VendorAttr.h" +#include "zypp/Package.h" + +#include "zypp/solver/detail/Helper.h" +#include "zypp/solver/detail/Resolver.h" + +#include "zypp/Target.h" + +///////////////////////////////////////////////////////////////////////// +namespace zypp +{ /////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + namespace solver + { ///////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////// + namespace detail + { /////////////////////////////////////////////////////////////////// + +using namespace std; +using namespace zypp; +using zypp::capability::SplitCap; + + +// check if downgrade is allowed +// +// both must have allowed vendor (e.g. 'SuSE', 'Novell', ...) and candidates buildtime must be +// newer. + +static bool +downgrade_allowed (PoolItem_Ref installed, PoolItem_Ref candidate) +{ + if ( installed->edition().compare (candidate->edition()) > 0 ) + return false; // candidate is newer + + static VendorAttr *va = VendorAttr::vendorAttr(); + + Resolvable::constPtr ires = installed.resolvable(); + Package::constPtr ipkg = asKind(ires); + Resolvable::constPtr cres = candidate.resolvable(); + Package::constPtr cpkg = asKind(cres); + + if ( va->isKnown( ipkg->vendor() ) + && va->isKnown( cpkg->vendor() ) ) + { +#warning Had Y2PM::runningFromSystem + return( ipkg->buildtime() >= cpkg->buildtime() ); + } + return false; +} + + + +struct FindObsoletes : public resfilter::OnCapMatchCallbackFunctor +{ + bool obsoletes; + + FindObsoletes () + : obsoletes (false) + { } + + bool operator()( PoolItem_Ref provider, const Capability & match ) + { + obsoletes = true; // we have a match + return false; // stop looping here + } +}; + + +// does the candidate obsolete the capability ? + +bool +Resolver::doesObsoleteCapability (PoolItem_Ref candidate, const Capability & cap) +{ + DBG << "doesObsoleteCapability " << candidate << ", " << cap << endl; + + Dep dep (Dep::OBSOLETES); + FindObsoletes info; + invokeOnEach( _pool.byCapabilityIndexBegin( cap.index(), dep ), + _pool.byCapabilityIndexEnd( cap.index(), dep ), + resfilter::callOnCapMatchIn( dep, cap, functor::functorRef(info) ) ); + + DBG << (info.obsoletes ? "YES" : "NO"); + return info.obsoletes; +} + + +bool +Resolver::doesObsoleteItem (PoolItem_Ref candidate, PoolItem_Ref installed) +{ + CapFactory factory; + Capability installedCap = factory.parse ( installed->kind(), installed->name(), Rel::EQ, installed->edition()); + + return doesObsoleteCapability (candidate, installedCap); +} + + +//----------------------------------------------------------------------------- + + +// find all available providers for installed name + +struct FindProviders : public resfilter::OnCapMatchCallbackFunctor +{ + PoolItemSet providers; // the providers which matched + + FindProviders () + { } + + bool operator()( PoolItem_Ref provider, const Capability & match ) + { + if ( provider.status().isToBeUninstalled() ) { + DBG << " IGNORE relation match (package is tagged to delete): " << match << " ==> " << provider << endl; + } + else { + DBG << " relation match: " << match << " ==> " << provider << endl; + providers.insert (provider); + } + return true; + } +}; + + +//----------------------------------------------------------------------------- + +/////////////////////////////////////////////////////////////////// +// +// +// METHOD NAME : Resolver::doUpgrade +// METHOD TYPE : int +// +// DESCRIPTION : go through all installed (but not yet touched by user) +// packages and look for update candidates +// handle splitprovides and replaced and dropped +// +void +Resolver::doUpgrade( UpgradeStatistics & opt_stats_r ) +{ + typedef map CandidateMap; + + typedef intrusive_ptr SplitCapPtr; + typedef map SplitMap; + typedef map TodoMap; + + CandidateMap candidatemap; + + PoolItemList _update_items; + + SplitMap splitmap; + TodoMap applyingSplits; + TodoMap addSplitted; + TodoMap addProvided; + TodoMap addMultiProvided; + + MIL << "doUpgrade start... " + << "(delete_unmaintained:" << (opt_stats_r.delete_unmaintained?"yes":"no") << ")" + << endl; + + _update_items.clear(); + { + UpgradeOptions opts( opt_stats_r ); + opt_stats_r = UpgradeStatistics(); + (UpgradeOptions&)opt_stats_r = opts; + } + + /////////////////////////////////////////////////////////////////// + // Reset all auto states and build PoolItemSet of available candidates + // (those that do not belong to PoolItems set to delete). + // + // On the fly rememeber splitprovides and afterwards check, which + // of them do apply. + /////////////////////////////////////////////////////////////////// + PoolItemSet available; // candidates available for install (no matter if selected for install or not) + + for ( ResPool::const_iterator it = _pool.begin(); it != _pool.end(); ++it ) { + PoolItem_Ref item = *it; + PoolItem_Ref candidate; + PoolItem_Ref installed; + item.status().setNoTransact(ResStatus::APPL_HIGH); + + if ( item.status().isToBeUninstalled() ) { + DBG << "doUpgrade available: SKIP to delete " << item << endl; + ++opt_stats_r.pre_todel; + continue; + } + + if ( item.status().isInstalled() ) { + installed = item; + candidate = Helper::findUpdateItem( _pool, installed); + if (!candidate) { + DBG << "doUpgrade available: SKIP no candidate for " << installed << endl; + ++opt_stats_r.pre_nocand; + continue; + } + candidatemap[installed] = candidate; + } + else { // assume Uninstalled + installed = PoolItem_Ref(); + candidate = item; + } + +#warning FIXME needs locks +#if 0 + if ( item->isLocked() ) { + DBG << "doUpgrade available: SKIP taboo candidate " << item << endl; + ++opt_stats_r.pre_nocand; + continue; + } +#endif + + ++opt_stats_r.pre_avcand; +#warning this should add the best candidate + available.insert( candidate ); + +MIL << "installed " << installed << ", candidate " << candidate << endl; + + // remember any splitprovides to packages actually installed. + CapSet caps = candidate->dep (Dep::PROVIDES); + for (CapSet::iterator cit = caps.begin(); cit != caps.end(); ++cit ) { +#warning How do I get to the SplitCap ? +#if 0 + SplitCapPtr scap (asKind(*cit)); + if (scap) { +MIL << "has split cap " << *scap << endl; + if (!installed) { + installed = Helper::findInstalledByNameAndKind (_pool, scap->name_str(), ResTraits::kind); + } + if ( installed ) { + splitmap[scap].insert( candidate ); + } + } +#endif + } + + } // iterate over the complete pool + +#warning Cant update from broken install medium like STABLE +#if 0 + // filter packages with requires that are not fulfilled by other candidates, + // to reduce errors a bit when trying to update from a broken installation + // medium (ie. STABLE) + { + CheckSetDeps::BrokenMap broken; + CheckSetDeps checker(available, broken); + + checker.setTrackRelations(false); + checker.checkAll(); + + if(!broken.empty()) + { + CheckSetDeps::BrokenMap::iterator bit, bend; + for(bit = broken.begin(), bend = broken.end(); bit != bend; ++bit) + { + DBG << bit->first->name() << " is broken, not considering it for update" << endl; + available.remove(bit->first); + --opt_stats_r.pre_avcand; + ++opt_stats_r.pre_nocand; + } + } + } +#endif + + MIL << "doUpgrade: " << opt_stats_r.pre_todel << " packages tagged to delete" << endl; + MIL << "doUpgrade: " << opt_stats_r.pre_nocand << " packages without candidate (foreign, replaced or dropped)" << endl; + MIL << "doUpgrade: " << opt_stats_r.pre_avcand << " packages available for update" << endl; + + MIL << "doUpgrade: going to check " << splitmap.size() << " probabely splitted packages" << endl; + { + /////////////////////////////////////////////////////////////////// + // splitmap entries are gouped by PoolItems (we know this). So get the + // filelist as a new PoolItem occurres, and use it for consecutive entries. + // + // On the fly buld SplitPkgMap from splits that do apply (i.e. file is + // in PoolItems's filelist). The way splitmap was created, candidates added + // are not initially tagged to delete! + /////////////////////////////////////////////////////////////////// + + PoolItem_Ref citem; + + for ( SplitMap::iterator it = splitmap.begin(); it != splitmap.end(); ++it ) { +#warning How to access the target ? +#if 0 + if (Target::providesFile (it->first->name_str(), it->first->path_str())) { + DBG << " " << it->second.size() << " package(s) for " << it->first << endl; + applyingSplits[citem].insert( it->second.begin(), it->second.end() ); + DBG << " split count for " << citem->name() << " now " << applyingSplits[citem].size() << endl; + } + else { + DBG << " " << it-> first << " does not apply" << endl; + } +#endif + } + splitmap.clear(); + } + + /////////////////////////////////////////////////////////////////// + // Now iterate installed packages, not selected to delete, and + // figure out what might be an appropriate replacement. Current + // packages state is changed immediately. Additional packages are + // reported but set to install later. + /////////////////////////////////////////////////////////////////// + MIL << "doUpgrade pass 1..." << endl; + + for ( ResPool::const_iterator it = _pool.begin(); it != _pool.end(); ++it ) { + + PoolItem_Ref installed(*it); + ResStatus status (installed.status()); + + if ( ! status.isInstalled() ) { + continue; + } + ++opt_stats_r.chk_installed_total; + + if ( status.transacts() ) { // we know its installed, if it transacts also + DBG << "SKIP to delete: " << it->resolvable() << endl; // it'll be deleted + ++opt_stats_r.chk_already_todel; + continue; + } + +#warning This needs locks +#if 0 + if ( (*it)->is_taboo() ) { + DBG << "SKIP taboo: " << (*it)->installedObj() << endl; + ++opt_stats_r.chk_is_taboo; + _update_items.push_back ( *it ); // remember in problem list ? + continue; + } +#endif + + CandidateMap::iterator cand_it = candidatemap.find(installed); + + bool probably_dropped = false; + + DBG << "REPLACEMENT FOR " << installed << endl; + /////////////////////////////////////////////////////////////////// + // figure out replacement + /////////////////////////////////////////////////////////////////// + if ( cand_it != candidatemap.end() ) { + + PoolItem_Ref candidate (cand_it->second); + + if ( ! candidate.status().isToBeInstalled() ) { + + if ( installed->edition().compare (candidate->edition()) < 0 ) { // new version + candidate.status().setToBeInstalled(ResStatus::APPL_HIGH); + DBG << " ==> INSTALL (new version): " << candidate << endl; + ++opt_stats_r.chk_to_update; + } else { + // check whether to downgrade: + + if (!downgrade_allowed (installed, candidate)) { + DBG << " ==> (keep installed)" << candidate << endl; + ++opt_stats_r.chk_to_keep_installed; + } else { + candidate.status().setToBeInstalled(ResStatus::APPL_HIGH); + DBG << " ==> INSTALL (SuSE version downgrade): " << candidate << endl; + ++opt_stats_r.chk_to_downgrade; + } + } + } else { + DBG << " ==> INSTALL (preselected): " << candidate << endl; + ++opt_stats_r.chk_already_toins; + } + + } + else { // no candidate + + // replaced or dropped (anyway there's no candidate for this!) + // If unique provides exists check if obsoleted (replaced). + // Remember new package for 2nd pass. + + Dep dep (Dep::PROVIDES); + CapFactory factory; + Capability installedCap = factory.parse ( installed->kind(), installed->name(), Rel::EQ, installed->edition()); + + FindProviders info; + + invokeOnEach( _pool.byCapabilityIndexBegin( installed->name(), dep ), + _pool.byCapabilityIndexEnd( installed->name(), dep ), + functor::chain (resfilter::ByUninstalled (), + resfilter::callOnCapMatchIn( dep, installedCap, functor::functorRef(info) ) ) ); + + int num_providers = info.providers.size(); + + DBG << "lookup " << num_providers << " provides for installed " << installedCap << endl; + + switch ( info.providers.size() ) { + case 0: + DBG << " ==> (dropped)" << endl; + // wait untill splits are processed. Might be a split obsoletes + // this one (i.e. package replaced but not provided by new one). + // otherwise it's finaly dropped. + probably_dropped = true; + break; + case 1: + addProvided[installed] = info.providers; + DBG << " ==> REPLACED by: " << (*info.providers.begin()) << endl; + // count stats later + // check obsoletes later + break; + default: + addMultiProvided[installed] = info.providers; + DBG << " ==> pass 2 (" << info.providers.size() << " times provided)" << endl; + // count stats later + // check obsoletes later + break; + } + + } // no candidate + + /////////////////////////////////////////////////////////////////// + // anyway check for packages split off + /////////////////////////////////////////////////////////////////// + + TodoMap::iterator sit = applyingSplits.find( installed ); + if ( sit != applyingSplits.end() ) { + PoolItemSet & toadd( sit->second ); + if ( !toadd.size() ) { + INT << "Empty SplitPkgMap entry for " << installed << endl; + } else { + for ( PoolItemSet::iterator ait = toadd.begin(); ait != toadd.end(); ++ait ) { + PoolItem_Ref split_candidate = *ait; + DBG << " ==> ADD (splitted): " << split_candidate << endl; + if ( probably_dropped + && split_candidate.status().isUninstalled() + && doesObsoleteItem (split_candidate, installed)) + { + probably_dropped = false; + } + } + addSplitted[installed] = toadd; + } + // count stats later + } + + /////////////////////////////////////////////////////////////////// + // now handle dropped package + /////////////////////////////////////////////////////////////////// + + if ( probably_dropped ) { + if ( opt_stats_r.delete_unmaintained ) { + installed.status().setToBeUninstalled(ResStatus::APPL_HIGH); + } + ++opt_stats_r.chk_dropped; + _update_items.push_back ( installed ); + } + + } // pass 1 end + + /////////////////////////////////////////////////////////////////// + // Now check the remembered packages and check non unique provided. + // Maybe one of them was somehow selected. Otherwise we have to guess + // one. + /////////////////////////////////////////////////////////////////// + MIL << "doUpgrade pass 2..." << endl; + + // look at the ones with a single provide first + + for ( TodoMap::iterator it = addProvided.begin(); it != addProvided.end(); ++it ) { + + PoolItemSet & tset( it->second ); // these are the providers (well, just one) + + for ( PoolItemSet::iterator sit = tset.begin(); sit != tset.end(); ++sit ) { + PoolItem_Ref provider (*sit); + + if (provider.status().setToBeInstalled(ResStatus::APPL_HIGH)) { + ++opt_stats_r.chk_replaced; + } + + // needs installed + + if ( doesObsoleteItem (provider, it->first ) ) { + it->first.status().setToBeUninstalled(ResStatus::APPL_HIGH); + } + } + + } + + // look at the split providers + + for ( TodoMap::iterator it = addSplitted.begin(); it != addSplitted.end(); ++it ) { + + PoolItemSet & tset( it->second ); + for ( PoolItemSet::iterator sit = tset.begin(); sit != tset.end(); ++sit ) { + if ((*sit).status().setToBeInstalled(ResStatus::APPL_HIGH)) { + ++opt_stats_r.chk_add_split; + } + } + + } + + // look at the ones with multiple providers + + for ( TodoMap::iterator it = addMultiProvided.begin(); it != addMultiProvided.end(); ++it ) { + DBG << "GET ONE OUT OF " << it->second.size() << " for " << it->first << endl; + + PoolItem_Ref guess; + PoolItemSet & gset( it->second ); + for ( PoolItemSet::iterator git = gset.begin(); git != gset.end(); ++git ) { + PoolItem_Ref item; + if ( item.status().isToBeInstalled()) { + DBG << " ==> (pass 2: meanwhile set to instaall): " << (*git) << endl; + if ( ! doesObsoleteItem (item, it->first ) ) { + it->first.status().setToBeUninstalled(ResStatus::APPL_HIGH); + } + guess = PoolItem_Ref(); + break; + } else { + // Be prepared to guess. + // Most common situation for guessing is something like: + // qt-devel + // qt-devel-experimental + // qt-devel-japanese + // That's why currently the shortest package name wins. + if ( !guess || guess->name().size() > item->name().size() ) { + guess = item; + } + } + } + + if ( guess ) { + guess.status().setToBeInstalled(ResStatus::APPL_HIGH); + DBG << " ==> REPLACED by: (pass 2: guessed): " << guess << endl; + if ( ! doesObsoleteItem (guess, it->first ) ) { + it->first.status().setToBeUninstalled(ResStatus::APPL_HIGH); + } + ++opt_stats_r.chk_replaced_guessed; + } + } + + /////////////////////////////////////////////////////////////////// + // done + /////////////////////////////////////////////////////////////////// + MIL << opt_stats_r << endl; +} + +/////////////////////////////////////////////////////////////////// + };// namespace detail + ///////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////// + };// namespace solver + /////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// +};// namespace zypp +///////////////////////////////////////////////////////////////////////// + + -- 2.7.4