ResolverUpgrade.cc

Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
00002 /*---------------------------------------------------------------------\
00003 |                          ____ _   __ __ ___                          |
00004 |                         |__  / \ / / . \ . \                         |
00005 |                           / / \ V /|  _/  _/                         |
00006 |                          / /__ | | | | | |                           |
00007 |                         /_____||_| |_| |_|                           |
00008 |                                                                      |
00009 \---------------------------------------------------------------------*/
00010 /* ResolverUpgrade.cc
00011  *
00012  * Implements the distribution upgrade algorithm.
00013  *
00014  * Copyright (C) 2005 SUSE Linux Products GmbH
00015  *
00016  * This program is free software; you can redistribute it and/or
00017  * modify it under the terms of the GNU General Public License,
00018  * version 2, as published by the Free Software Foundation.
00019  *
00020  * This program is distributed in the hope that it will be useful, but
00021  * WITHOUT ANY WARRANTY; without even the implied warranty of
00022  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00023  * General Public License for more details.
00024  *
00025  * You should have received a copy of the GNU General Public License
00026  * along with this program; if not, write to the Free Software
00027  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00028  * 02111-1307, USA.
00029  */
00030 
00031 /*
00032   stolen from PMPackageManager_update.cc
00033   original author Michael Andres <ma@suse.de>
00034   zypp port by Klaus Kaempf <kkaempf@suse.de>
00035 
00036 /-*/
00037 
00038 #include "zypp/CapSet.h"
00039 #include "zypp/capability/SplitCap.h"
00040 
00041 #include "zypp/base/Logger.h"
00042 #include "zypp/base/String.h"
00043 #include "zypp/base/Gettext.h"
00044 #include "zypp/base/Exception.h"
00045 #include "zypp/VendorAttr.h"
00046 #include "zypp/base/Algorithm.h"
00047 #include "zypp/ResPool.h"
00048 #include "zypp/ResStatus.h"
00049 #include "zypp/ResFilters.h"
00050 #include "zypp/CapFilters.h"
00051 #include "zypp/Capability.h"
00052 #include "zypp/CapFactory.h"
00053 #include "zypp/VendorAttr.h"
00054 #include "zypp/Package.h"
00055 
00056 #include "zypp/capability/CapabilityImpl.h"
00057 #include "zypp/ZYppFactory.h"
00058 
00059 #include "zypp/solver/detail/Types.h"
00060 #include "zypp/solver/detail/Helper.h"
00061 #include "zypp/solver/detail/Resolver.h"
00062 
00063 #include "zypp/Target.h"
00064 
00066 namespace zypp
00067 { 
00068 
00069   namespace solver
00070   { 
00071 
00072     namespace detail
00073     { 
00074 
00075 using namespace std;
00076 using namespace zypp;
00077 using zypp::capability::SplitCap;
00078 
00079 
00085 struct AVOrder : public std::binary_function<PoolItem_Ref,PoolItem_Ref,bool>
00086 {
00087     // NOTE: operator() provides LESS semantics to order the set.
00088     // So LESS means 'prior in set'. We want 'better' archs and
00089     // 'better' editions at the beginning of the set. So we return
00090     // TRUE if (lhs > rhs)!
00091     //
00092     bool operator()( const PoolItem_Ref lhs, const PoolItem_Ref rhs ) const
00093         {
00094             int res = lhs->arch().compare( rhs->arch() );
00095             if ( res )
00096                 return res > 0;
00097             res = lhs->edition().compare( rhs->edition() );
00098             if ( res )
00099                 return res > 0;
00100 
00101             // no more criteria, still equal:
00102             // use the ResObject::constPtr (the poiner value)
00103             // (here it's arbitrary whether < or > )
00104             return lhs.resolvable() < rhs.resolvable();
00105         }
00106 };
00107 
00108 typedef std::set<PoolItem_Ref, AVOrder> PoolItemOrderSet;
00109 
00110 
00111 
00112 // check if downgrade is allowed
00113 // (Invariant on entry: installed.edition >= candidate.edition)
00114 //
00115 // candidate must have allowed vendor (e.g. 'SuSE', 'Novell', ...) and candidates buildtime must be
00116 // newer.
00117 
00118 static bool
00119 downgrade_allowed( PoolItem_Ref installed, PoolItem_Ref candidate, bool silent_downgrades )
00120 {
00121     if (installed.status().isLocked()) {
00122         MIL << "Installed " << installed << " is locked, not upgrading" << endl;
00123         return false;
00124     }
00125 
00126     Resolvable::constPtr ires = installed.resolvable();
00127     Package::constPtr ipkg = asKind<Package>(ires);
00128     Resolvable::constPtr cres = candidate.resolvable();
00129     Package::constPtr cpkg = asKind<Package>(cres);
00130 
00131     if (ipkg)
00132       DBG << "Installed vendor '" << ipkg->vendor() << "'" << endl;
00133     if (cpkg)
00134       DBG << "Candidate vendor '" << cpkg->vendor() << "'" << endl;
00135 
00136     if (cpkg
00137         && VendorAttr::instance().isKnown( cpkg->vendor() ) )
00138     {
00139         if ( silent_downgrades )
00140             return true;
00141         if ( ipkg->buildtime() < cpkg->buildtime() ) {                  // installed has older buildtime
00142             MIL << "allowed downgrade " << installed << " to " << candidate << endl;
00143             return true;                                                // see bug #152760
00144         }
00145     }
00146     return false;
00147 }
00148 
00149 
00150 
00151 struct FindObsoletes
00152 {
00153     bool obsoletes;
00154 
00155     FindObsoletes ()
00156         : obsoletes (false)
00157     { }
00158 
00159     bool operator()( const CapAndItem & cai )
00160     {
00161         obsoletes = true;                               // we have a match
00162         return false;                                   // stop looping here
00163     }
00164 };
00165 
00166 
00167 // does the candidate obsolete the capability ?
00168 
00169 bool
00170 Resolver::doesObsoleteCapability (PoolItem_Ref candidate, const Capability & cap)
00171 {
00172     _DEBUG("doesObsoleteCapability " << candidate << ", " << cap);
00173 
00174     Dep dep (Dep::OBSOLETES);
00175     FindObsoletes info;
00176     invokeOnEach( _pool.byCapabilityIndexBegin( cap.index(), dep ),
00177                   _pool.byCapabilityIndexEnd( cap.index(), dep ),
00178                   resfilter::ByCapMatch( cap ),
00179                   functor::functorRef<bool,CapAndItem>(info) );
00180 
00181     _DEBUG((info.obsoletes ? "YES" : "NO"));
00182     return info.obsoletes;
00183 }
00184 
00185 
00186 bool
00187 Resolver::doesObsoleteItem (PoolItem_Ref candidate, PoolItem_Ref installed)
00188 {
00189     CapFactory factory;
00190     Capability installedCap =  factory.parse ( installed->kind(), installed->name(), Rel::EQ, installed->edition());
00191 
00192     return doesObsoleteCapability (candidate, installedCap);
00193 }
00194 
00195 //-----------------------------------------------------------------------------
00196 
00197 // find best available providers for installed name
00198 
00199 typedef map<string, PoolItem_Ref> FindMap;
00200 
00201 struct FindProviders
00202 {
00203     FindMap providers;          // the best providers which matched
00204     PoolItem forItem;
00205     bool otherVendorFound;
00206     FindProviders (PoolItem item)
00207         :forItem(item),
00208          otherVendorFound(false)
00209     { }
00210 
00211     bool operator()( const CapAndItem & cai )
00212     {
00213         PoolItem provider( cai.item );
00214         if ( !VendorAttr::instance().equivalent(provider->vendor(), forItem->vendor()) )
00215         {
00216             MIL << "Discarding '" << provider << "' from vendor '"
00217                 << provider->vendor() << "' different to uninstalled '"
00218                 << forItem->vendor() << "' vendor." << endl;
00219             otherVendorFound = true;
00220         } else if ( provider.status().isToBeUninstalled() ) {
00221             MIL << "  IGNORE relation match (package is tagged to delete): " << cai.cap << " ==> " << provider << endl;
00222         }
00223         else {
00224             FindMap::iterator it = providers.find( provider->name() );
00225 
00226             if (it != providers.end()) {                                // provider with same name found
00227                 if (provider.status().isToBeInstalled()
00228                     || it->second.status().isToBeInstalled()) {
00229 
00230                     if (provider.status().isToBeInstalled()
00231                         && it->second.status().isToBeInstalled()) {
00232                         ERR << "only one should be set for installation: " << it->second << "; " << provider << endl;
00233                     } else {
00234                         if (provider.status().isToBeInstalled()) {
00235                             it->second = provider; // take thatone which is already set for installation
00236                         }
00237                     }
00238                 } else {
00239                     // not the same --> find better provider
00240                     int cmp = it->second->arch().compare( provider->arch() );
00241                     if (cmp < 0) {                                              // new provider has better arch
00242                         it->second = provider;
00243                     }
00244                     else if (cmp == 0) {                                        // new provider has equal arch
00245                         if (it->second->edition().compare( provider->edition() ) < 0) {
00246                             it->second = provider;                              // new provider has better edition
00247                         }
00248                     }
00249                 }
00250             }
00251             else {
00252                 providers[provider->name()] = provider;
00253             }
00254         }
00255         return true;
00256     }
00257 };
00258 
00259 
00260 //-----------------------------------------------------------------------------
00261 
00262 // Selecting item for installation
00263 
00264 class LookForSelected : public resfilter::PoolItemFilterFunctor
00265 {
00266   public:
00267     bool found;
00268     PoolItem_Ref candidate;
00269     
00270     LookForSelected (PoolItem_Ref can)
00271         : candidate (can),
00272         found (false)
00273     { }
00274 
00275     bool operator()( PoolItem_Ref item )
00276     {
00277         if (item.status().isToBeInstalled()
00278             && item->edition() == candidate->edition()
00279             && item->arch() == candidate->arch()) {
00280             MIL << item << " is already selected for installation --> ignoring" << endl;            
00281             found = true;
00282             return false; // stop here
00283         }
00284         return true;
00285     }
00286 };
00287 
00288 bool setForInstallation (const ResPool &pool, PoolItem_Ref item) {
00289     LookForSelected info(item);
00290 
00291     invokeOnEach( pool.byNameBegin (item->name()),
00292                   pool.byNameEnd (item->name()),
00293                   functor::chain (resfilter::ByUninstalled (),                  // ByUninstalled
00294                                   resfilter::ByKind (item->kind())),            // equal kind
00295                   functor::functorRef<bool,PoolItem> (info) );
00296     if (info.found) {
00297         MIL << "   ---> " << item << " will be ignoring" << endl;
00298         return true;
00299     } else {
00300         return item.status().setToBeInstalled( ResStatus::APPL_HIGH );
00301     }
00302 }       
00303 
00304 //-----------------------------------------------------------------------------
00305 
00307 //
00308 //
00309 //      METHOD NAME : Resolver::doUpgrade
00310 //      METHOD TYPE : int
00311 //
00312 //      DESCRIPTION : go through all installed (but not yet touched by user)
00313 //              packages and look for update candidates
00314 //              handle splitprovides and replaced and dropped
00315 //
00316 void
00317 Resolver::doUpgrade( UpgradeStatistics & opt_stats_r )
00318 {
00319   typedef map<PoolItem_Ref,PoolItem_Ref> CandidateMap;
00320   typedef intrusive_ptr<const SplitCap> SplitCapPtr;
00321   typedef map<PoolItem_Ref,PoolItemOrderSet> SplitMap;
00322   typedef map<PoolItem_Ref,PoolItemOrderSet> TodoMap;
00323 
00324   CandidateMap candidatemap;
00325 
00326   SplitMap    splitmap;
00327   TodoMap     applyingSplits;
00328   TodoMap     addSplitted;
00329   TodoMap     addProvided;
00330   TodoMap     addMultiProvided;
00331 
00332   Target_Ptr target;
00333   try {
00334         target = getZYpp()->target();
00335   }
00336   catch( const Exception & excpt_r) {
00337         ERR << "Huh, no target ?";
00338         ZYPP_CAUGHT(excpt_r);
00339         if (!_testing) return;          // can't continue without target
00340         MIL << "Running in test mode, continuing without target" << endl;
00341   }
00342   MIL << "target at " << target << endl;
00343 
00344   MIL << "doUpgrade start... "
00345     << "(delete_unmaintained:" << (opt_stats_r.delete_unmaintained?"yes":"no") << ")"
00346     << "(silent_downgrades:" << (opt_stats_r.silent_downgrades?"yes":"no") << ")"
00347     << "(keep_installed_patches:" << (opt_stats_r.keep_installed_patches?"yes":"no") << ")"
00348     << endl;
00349 
00350   _update_items.clear();
00351   {
00352     UpgradeOptions opts( opt_stats_r );
00353     opt_stats_r = UpgradeStatistics();
00354     (UpgradeOptions&)opt_stats_r = opts;
00355   }
00356 
00358   // Reset all auto states and build PoolItemOrderSet of available candidates
00359   // (those that do not belong to PoolItems set to delete).
00360   //
00361   // On the fly remember splitprovides and afterwards check, which
00362   // of them do apply.
00364   PoolItemOrderSet available; // candidates available for install (no matter if selected for install or not)
00365 
00366   for ( ResPool::const_iterator it = _pool.begin(); it != _pool.end(); ++it ) {
00367     PoolItem_Ref item = *it;
00368     PoolItem_Ref candidate;
00369     PoolItem_Ref installed;
00370 
00371     if ( item.status().isToBeUninstalled() ) {
00372       MIL << "doUpgrade available: SKIP to delete " << item << endl;
00373       ++opt_stats_r.pre_todel;
00374       continue;
00375     }
00376     if ( item.status().isLocked() ) {
00377       MIL << "doUpgrade available: SKIP locked " << item << endl;
00378       if ( item.status().staysInstalled() ) {
00379         ++opt_stats_r.pre_nocand;
00380       }
00381       continue;
00382     }
00383 
00384     if ( item.status().staysInstalled() ) {     // installed item
00385       installed = item;
00386       CandidateMap::const_iterator cand_it = candidatemap.find( installed );
00387       if (cand_it != candidatemap.end()) {
00388         candidate = cand_it->second;                            // found candidate already
00389       }
00390       else {
00391         candidate = Helper::findUpdateItem( _pool, installed ); // find 'best' upgrade candidate
00392       }
00393       if (!candidate) {
00394         MIL << "doUpgrade available: SKIP no candidate for " << installed << endl;
00395         ++opt_stats_r.pre_nocand;
00396         continue;
00397       }
00398       MIL << "item " << item << " is installed, candidate is " << candidate << endl;
00399       if (candidate.status().isSeen()) {                        // seen already
00400         candidate.status().setSeen(true);
00401         continue;
00402       }
00403       candidate.status().setSeen(true);                         // mark as seen
00404       candidatemap[installed] = candidate;
00405     }
00406     else {                                      // assume Uninstalled
00407       if (item.status().isSeen()) {                             // seen already
00408         item.status().setSeen(true);
00409         continue;
00410       }
00411       candidate = item;
00412       candidate.status().setSeen(true);                         // mark as seen
00413       installed = Helper::findInstalledItem( _pool, candidate );
00414       if (installed) {                                          // check if we already have an installed
00415         if ( installed.status().isLocked() ) {
00416           MIL << "doUpgrade available: SKIP candidate " << candidate << ", locked " << installed << endl;
00417           continue;
00418         }
00419 
00420         if ( !VendorAttr::instance().equivalent(installed->vendor(), candidate->vendor()) )
00421         {
00422             MIL << "Discarding '" << candidate << "' from vendor '"
00423                 << candidate->vendor() << "' different to uninstalled '"
00424                 << installed->vendor() << "' vendor." << endl;
00425             continue;
00426         }
00427 
00428         MIL << "found installed " << installed << " for item " << candidate << endl;
00429         CandidateMap::const_iterator cand_it = candidatemap.find( installed );
00430         if (cand_it == candidatemap.end()                                               // not in map yet
00431             || (cand_it->second->arch().compare( candidate->arch() ) < 0)               // or the new has better architecture
00432             || ((cand_it->second->arch().compare( candidate->arch() ) == 0)             // or the new has the same architecture
00433                 && (cand_it->second->edition().compare( candidate->edition() ) < 0))    //   and a better edition (-> 157501)
00434             )
00435         {
00436             candidatemap[installed] = candidate;                                // put it in !
00437         }
00438       }
00439     }
00440 
00441     ++opt_stats_r.pre_avcand;
00442     available.insert( candidate );
00443 
00444     MIL << "installed " << installed << ", candidate " << candidate << endl;
00445 
00446     // remember any splitprovides to packages actually installed.
00447     CapSet caps = candidate->dep (Dep::PROVIDES);
00448     for (CapSet::iterator cit = caps.begin(); cit != caps.end(); ++cit ) {
00449         if (isKind<capability::SplitCap>( *cit ) ) {
00450 
00451             capability::CapabilityImpl::SplitInfo splitinfo = capability::CapabilityImpl::getSplitInfo( *cit );
00452 
00453             PoolItem splititem = Helper::findInstalledByNameAndKind (_pool, splitinfo.name, ResTraits<zypp::Package>::kind);
00454             MIL << "has split cap " << splitinfo.name << ":" << splitinfo.path << ", splititem:" << splititem << endl;
00455             if (splititem) {
00456                 if (target) {
00457                     ResObject::constPtr robj = target->whoOwnsFile( splitinfo.path );
00458                     if (robj)
00459                       MIL << "whoOwnsFile(): " << *robj << endl;
00460                     if (robj
00461                         && robj->name() == splitinfo.name)
00462                     {
00463                       MIL << "split matched !" << endl;
00464                         splitmap[splititem].insert( candidate );
00465                     }
00466                 }
00467             }
00468         }
00469     }
00470 
00471   } // iterate over the complete pool
00472 
00473   // reset all seen (for next run)
00474   for ( ResPool::const_iterator it = _pool.begin(); it != _pool.end(); ++it ) {
00475         it->status().setSeen( false );
00476   }
00477 
00478   MIL << "doUpgrade: " << opt_stats_r.pre_todel  << " packages tagged to delete" << endl;
00479   MIL << "doUpgrade: " << opt_stats_r.pre_nocand << " packages without candidate (foreign, replaced or dropped)" << endl;
00480   MIL << "doUpgrade: " << opt_stats_r.pre_avcand << " packages available for update" << endl;
00481 
00482   MIL << "doUpgrade: going to check " << splitmap.size() << " probably splitted packages" << endl;
00483   {
00485     // splitmap entries are gouped by PoolItems (we know this). So get the
00486     // filelist as a new PoolItem occures, and use it for consecutive entries.
00487     //
00488     // On the fly build SplitPkgMap from splits that do apply (i.e. file is
00489     // in PoolItems's filelist). The way splitmap was created, candidates added
00490     // are not initially tagged to delete!
00492 
00493     PoolItem_Ref citem;
00494 
00495     for ( SplitMap::iterator it = splitmap.begin(); it != splitmap.end(); ++it ) {
00496         applyingSplits[it->first].insert( it->second.begin(), it->second.end() );
00497         _DEBUG("  split count for " << it->first->name() << " now " << applyingSplits[it->first].size());
00498     }
00499     splitmap.clear();
00500   }
00501 
00503   // Now iterate installed packages, not selected to delete, and
00504   // figure out what might be an appropriate replacement. Current
00505   // packages state is changed immediately. Additional packages are
00506   // reported but set to install later.
00508   MIL << "doUpgrade pass 1..." << endl;
00509 
00510   for ( ResPool::const_iterator it = _pool.begin(); it != _pool.end(); ++it ) {
00511 
00512     PoolItem_Ref installed(*it);
00513     ResStatus status (installed.status());
00514 
00515     if ( ! status.staysInstalled() ) {
00516       continue;
00517     }
00518     ++opt_stats_r.chk_installed_total;
00519 
00520     if ( status.transacts() ) {                                 // we know its installed, if it transacts also
00521       MIL << "SKIP to delete: " << installed.resolvable() << endl;      // it'll be deleted
00522       ++opt_stats_r.chk_already_todel;
00523       continue;
00524     }
00525 
00526     if ( installed.status().isLocked() ) {                      // skip locked
00527       MIL << "SKIP taboo: " << installed << endl;
00528       ++opt_stats_r.chk_is_taboo;
00529       _update_items.push_back( installed );                     // remember in problem list
00530       continue;
00531     }
00532 
00533     if ( isKind<Patch>(installed.resolvable())
00534          || isKind<Atom>(installed.resolvable())
00535          || isKind<Script>(installed.resolvable())
00536          || isKind<Message>(installed.resolvable()) )
00537       {
00538         if ( ! opt_stats_r.keep_installed_patches )
00539           {
00540             if ( isKind<Patch>(installed.resolvable()) )
00541               MIL << "OUTDATED Patch: " << installed << endl;
00542             installed.status().setToBeUninstalled( ResStatus::APPL_HIGH );
00543           }
00544         else
00545           {
00546             if ( isKind<Patch>(installed.resolvable()) )
00547               MIL << "SKIP Patch: " << installed << endl;
00548           }
00549         continue;
00550       }
00551 
00552     CandidateMap::iterator cand_it = candidatemap.find( installed );
00553 
00554     bool probably_dropped = false;
00555 
00556     MIL << "REPLACEMENT FOR " << installed << endl;
00558     // figure out replacement
00560     if ( cand_it != candidatemap.end() ) {
00561 
00562       PoolItem_Ref candidate (cand_it->second);
00563 
00564       if ( ! candidate.status().isToBeInstalled() ) {
00565         int cmp = installed->edition().compare( candidate->edition() );
00566         if ( cmp < 0 ) {   // new edition
00567           setForInstallation (_pool,candidate);
00568           MIL << " ==> INSTALL (new version): " << candidate << endl;
00569           ++opt_stats_r.chk_to_update;
00570         } else {                                                        // older or equal edition
00571           // check whether to downgrade:
00572 
00573           if (cmp == 0                                                  // equal
00574               || !downgrade_allowed( installed, candidate,
00575                                      opt_stats_r.silent_downgrades) )   //  or downgrade not allowed
00576           {
00577             MIL << " ==> (keep installed)" << candidate << endl;        // keep installed
00578             ++opt_stats_r.chk_to_keep_installed;
00579           } else {// older and downgrade allowed
00580             setForInstallation (_pool, candidate);
00581             MIL << " ==> INSTALL (SuSE version downgrade): " << candidate << endl;
00582             ++opt_stats_r.chk_to_downgrade;
00583           }
00584         }
00585       } else {
00586         MIL << " ==> INSTALL (preselected): " << candidate << endl;
00587         ++opt_stats_r.chk_already_toins;
00588       }
00589 
00590     }
00591     else {              // no candidate
00592 
00593       // replaced or dropped (anyway there's no candidate for this!)
00594       // If unique provides exists check if obsoleted (replaced).
00595       // Remember new package for 2nd pass.
00596 
00597       Dep dep (Dep::PROVIDES);
00598       CapFactory factory;
00599       Capability installedCap = factory.parse( installed->kind(), installed->name(), Rel::GE, installed->edition() );
00600 
00601       FindProviders info(installed);
00602 
00603       invokeOnEach( _pool.byCapabilityIndexBegin( installed->name(), dep ),
00604                     _pool.byCapabilityIndexEnd( installed->name(), dep ),
00605                     functor::chain( resfilter::ByCaIUninstalled(),
00606                                     resfilter::ByCapMatch( installedCap ) ) ,
00607                     functor::functorRef<bool,CapAndItem>(info) );
00608 
00609       int num_providers = info.providers.size();
00610 
00611       _DEBUG("lookup " << num_providers << " provides for installed " << installedCap);
00612 
00613       // copy from map to set
00614       PoolItemOrderSet providers;
00615       for (FindMap::const_iterator mapit = info.providers.begin(); mapit != info.providers.end(); ++mapit) {
00616         providers.insert( mapit->second );
00617       }
00618 
00619       switch ( info.providers.size() ) {
00620       case 0:
00621           if (info.otherVendorFound) {
00622               MIL << " only resolvable with other vendor found ==> do nothing" << endl;
00623           } else {
00624               MIL << " ==> (dropped)" << endl;
00625               // wait untill splits are processed. Might be a split obsoletes
00626               // this one (i.e. package replaced but not provided by new one).
00627               // otherwise it's finaly dropped.
00628               probably_dropped = true;
00629           }
00630         break;
00631       case 1:
00632         addProvided[installed] = providers;
00633         MIL << " ==> REPLACED by: " << (*providers.begin()) << endl;
00634         // count stats later
00635         // check obsoletes later
00636         break;
00637       default:
00638         addMultiProvided[installed] = providers;
00639         MIL << " ==> pass 2 (" << providers.size() << " times provided)" << endl;
00640         // count stats later
00641         // check obsoletes later
00642         break;
00643       }
00644 
00645     }   // no candidate
00646 
00648     // anyway check for packages split off
00650 
00651     TodoMap::iterator sit = applyingSplits.find( installed );
00652     if ( sit != applyingSplits.end() ) {
00653       PoolItemOrderSet & toadd( sit->second );
00654       if ( !toadd.size() ) {
00655         INT << "Empty SplitPkgMap entry for " << installed << endl;
00656       } else {
00657         FindMap candidate;
00658         for ( PoolItemOrderSet::iterator ait = toadd.begin(); ait != toadd.end(); ++ait ) {
00659             PoolItem_Ref split_candidate = *ait;
00660             if ( probably_dropped
00661                  && split_candidate.status().staysUninstalled()
00662                  && doesObsoleteItem (split_candidate, installed))
00663             {
00664                 probably_dropped = false;
00665             }
00666 
00667             FindMap::iterator itcandidate = candidate.find( split_candidate->name() );
00668 
00669             if (itcandidate != candidate.end()) {       // split canidate with the same name found
00670                 if (split_candidate.status().isToBeInstalled()
00671                     || itcandidate->second.status().isToBeInstalled()) {
00672 
00673                     if (split_candidate.status().isToBeInstalled()
00674                         && itcandidate->second.status().isToBeInstalled()) {
00675                         ERR << "only one should be set for installation: " << itcandidate->second << "; " << split_candidate << endl;
00676                     } else {
00677                         if (split_candidate.status().isToBeInstalled()) {
00678                             itcandidate->second = split_candidate; // take thatone which is already set for installation
00679                         }
00680                     }
00681                 } else {
00682                     // not the same --> find better provider
00683                     if (itcandidate->second->arch() != installed->arch()
00684                         && split_candidate->arch() == installed->arch() ) {
00685                         // prefer candidate which the same architecture as the installed item
00686                         itcandidate->second = split_candidate;
00687                     } else {
00688                         int cmp = itcandidate->second->arch().compare( split_candidate->arch() );
00689                         if (cmp < 0) {                                          // new provider has better arch
00690                             itcandidate->second = split_candidate;
00691                         }
00692                         else if (cmp == 0) {                                    // new provider has equal arch
00693                             if (itcandidate->second->edition().compare( split_candidate->edition() ) < 0) {
00694                                 itcandidate->second = split_candidate;          // new provider has better edition
00695                             }
00696                         }
00697                     }
00698                 }
00699             }
00700             else {
00701                 candidate[split_candidate->name()] = split_candidate;
00702             }
00703         }
00704 
00705         PoolItemOrderSet addcandidate;
00706         for (FindMap::iterator itcandidate = candidate.begin() ; itcandidate != candidate.end(); itcandidate++) {
00707             addcandidate.insert(itcandidate->second);
00708             MIL << " ==> ADD (splitted): " << itcandidate->second << endl;
00709         }
00710 
00711         addSplitted[installed] = addcandidate;
00712       }
00713       // count stats later
00714     }
00715 
00717     // now handle dropped package
00719 
00720     if ( probably_dropped ) {
00721       if ( opt_stats_r.delete_unmaintained
00722            && VendorAttr::instance().equivalent( installed->vendor(), "suse" ) ) {
00723         installed.status().setToBeUninstalled( ResStatus::APPL_HIGH );
00724       }
00725       ++opt_stats_r.chk_dropped;
00726       _update_items.push_back( installed );
00727     }
00728 
00729   } // pass 1 end
00730 
00732   // Now check the remembered packages and check non unique provided.
00733   // Maybe one of them was somehow selected. Otherwise we have to guess
00734   // one.
00736   MIL << "doUpgrade pass 2..." << endl;
00737 
00738   // look at the ones with a single provide first
00739 
00740   for ( TodoMap::iterator it = addProvided.begin(); it != addProvided.end(); ++it ) {
00741 
00742     PoolItemOrderSet & tset( it->second );              // these are the providers (well, just one)
00743 
00744     for ( PoolItemOrderSet::iterator sit = tset.begin(); sit != tset.end(); ++sit ) {
00745       PoolItem_Ref provider (*sit);
00746 
00747       if (setForInstallation (_pool, provider)) {
00748         ++opt_stats_r.chk_replaced;
00749       }
00750 
00751       // needs installed
00752 
00753       if ( doesObsoleteItem (provider, it->first ) ) {
00754         it->first.status().setToBeUninstalled( ResStatus::APPL_HIGH );
00755       }
00756     }
00757 
00758   }
00759 
00760   // look at the split providers
00761 
00762   for ( TodoMap::iterator it = addSplitted.begin(); it != addSplitted.end(); ++it ) {
00763 
00764     PoolItemOrderSet & tset( it->second );
00765     PoolItem_Ref lastItem = PoolItem_Ref();
00766 
00767     for ( PoolItemOrderSet::iterator sit = tset.begin(); sit != tset.end(); ++sit ) {
00768         if (!lastItem
00769             || compareByN ( lastItem.resolvable(), sit->resolvable()) != 0) // do not install packages with the same NVR and other architecture
00770         {
00771             PoolItem_Ref item( *sit );
00772 
00773             // only install split if its actually a different edition
00774 
00775             PoolItem_Ref already_installed = Helper::findInstalledItem( _pool, item );
00776             if (!already_installed
00777                 || already_installed->edition().compare( item->edition() ) != 0 )
00778             {
00779                 if (setForInstallation (_pool, item)) {
00780                     ++opt_stats_r.chk_add_split;
00781                 }
00782             }
00783         }
00784         lastItem = *sit;
00785     }
00786 
00787   }
00788 
00789   // look at the ones with multiple providers
00790 
00791   for ( TodoMap::iterator it = addMultiProvided.begin(); it != addMultiProvided.end(); ++it ) {
00792     MIL << "GET ONE OUT OF " << it->second.size() << " for " << it->first << endl;
00793 
00794     PoolItem_Ref guess;
00795     PoolItemOrderSet & gset( it->second );
00796 
00797     for ( PoolItemOrderSet::iterator git = gset.begin(); git != gset.end(); ++git ) {
00798       PoolItem_Ref item (*git);
00799 
00800       if (git == gset.begin())          // default to first of set; the set is ordered, first is the best
00801         guess = item;
00802 
00803       if ( item.status().isToBeInstalled()) {
00804         MIL << " ==> (pass 2: meanwhile set to install): " << item << endl;
00805         if ( ! doesObsoleteItem (item, it->first ) ) {
00806           it->first.status().setToBeUninstalled( ResStatus::APPL_HIGH );
00807         }
00808         guess = PoolItem_Ref();
00809         break;
00810       } else {
00811         // Be prepared to guess.
00812         // Most common situation for guessing is something like:
00813         //   qt-devel
00814         //   qt-devel-experimental
00815         //   qt-devel-japanese
00816         // That's why currently the shortest package name wins.
00817         if ( !guess || guess->name().size() > item->name().size() ) {
00818           guess = item;
00819         }
00820       }
00821     }
00822 
00823     if ( guess ) {
00824         // Checking if the selected provider depends on language, if yes try to find out the
00825         // correct language package
00826         bool requested_locale_match = false;
00827         CapSet freshens( guess->dep( Dep::FRESHENS ) );
00828 
00829         // is this a language package ?
00830         for (CapSet::const_iterator cit = freshens.begin(); cit != freshens.end(); ++cit) {
00831             if (cit->refers() == ResTraits<Language>::kind) {
00832                 requested_locale_match = true;
00833                 break;
00834             }
00835         }
00836 
00837         if (requested_locale_match) {
00838             // searching the best language
00839             PoolItemOrderSet & gset( it->second );
00840             requested_locale_match = false;
00841 
00842             for ( PoolItemOrderSet::iterator git = gset.begin(); git != gset.end(); ++git ) {
00843                 PoolItem_Ref item (*git);
00844 
00845                 if ( item.status().isToBeInstalled()) {
00846                     MIL << " ==> (pass 2: meanwhile set to install): " << item << endl;
00847                     if ( ! doesObsoleteItem (item, it->first ) ) {
00848                         it->first.status().setToBeUninstalled( ResStatus::APPL_HIGH );
00849                     }
00850                     guess = PoolItem_Ref();
00851                     break;
00852                 } else {
00853                     freshens = item->dep( Dep::FRESHENS );
00854                     ZYpp::Ptr z = zypp::getZYpp();
00855                     ZYpp::LocaleSet requested_locales = z->getRequestedLocales();
00856 
00857                     // try to find a match of the locale freshens with one of the requested locales
00858 
00859                     for (CapSet::const_iterator cit = freshens.begin(); cit != freshens.end(); ++cit) {
00860                         if (cit->refers() == ResTraits<Language>::kind) {
00861                             string loc = cit->index();
00862                             MIL << "Look for language fallback " << loc << ":" << item << endl;
00863                             if (requested_locales.find( Locale( loc ) ) != requested_locales.end()) {
00864                                 MIL << "Locale '" << loc << "' is requested" << endl;
00865                                 requested_locale_match = true;
00866                                 guess = item;
00867                                 break;
00868                             }
00869                         }
00870                     }
00871                 }
00872                 if (requested_locale_match) break;
00873             }
00874         }
00875     }
00876 
00877     if ( guess ) {
00878       setForInstallation (_pool, guess);
00879       MIL << " ==> REPLACED by: (pass 2: guessed): " << guess << endl;
00880       if ( ! doesObsoleteItem (guess, it->first ) ) {
00881         it->first.status().setToBeUninstalled( ResStatus::APPL_HIGH );
00882       }
00883       ++opt_stats_r.chk_replaced_guessed;
00884     }
00885   }
00886 
00888   // done
00890   MIL << opt_stats_r << endl;
00891 
00892   // Setting Resolver to upgrade mode
00893   _upgradeMode = true;
00894 }
00895 
00897     };// namespace detail
00900   };// namespace solver
00903 };// namespace zypp
00905 
00906 

Generated on Tue Sep 25 19:23:08 2007 for libzypp by  doxygen 1.5.3