00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
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
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
00088
00089
00090
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
00102
00103
00104 return lhs.resolvable() < rhs.resolvable();
00105 }
00106 };
00107
00108 typedef std::set<PoolItem_Ref, AVOrder> PoolItemOrderSet;
00109
00110
00111
00112
00113
00114
00115
00116
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() ) {
00142 MIL << "allowed downgrade " << installed << " to " << candidate << endl;
00143 return true;
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;
00162 return false;
00163 }
00164 };
00165
00166
00167
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
00198
00199
00200
00201 typedef map<string, PoolItem_Ref> FindMap;
00202
00203 struct FindProviders
00204 {
00205 FindMap providers;
00206
00207 FindProviders ()
00208 { }
00209
00210 bool operator()( const CapAndItem & cai )
00211 {
00212 PoolItem provider( cai.item );
00213 if ( provider.status().isToBeUninstalled() ) {
00214 MIL << " IGNORE relation match (package is tagged to delete): " << cai.cap << " ==> " << provider << endl;
00215 }
00216 else {
00217 FindMap::iterator it = providers.find( provider->name() );
00218
00219 if (it != providers.end()) {
00220 int cmp = it->second->arch().compare( provider->arch() );
00221 if (cmp < 0) {
00222 it->second = provider;
00223 }
00224 else if (cmp == 0) {
00225 if (it->second->edition().compare( provider->edition() ) < 0) {
00226 it->second = provider;
00227 }
00228 }
00229 }
00230 else {
00231 providers[provider->name()] = provider;
00232 }
00233 }
00234 return true;
00235 }
00236 };
00237
00238
00239
00240
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251 void
00252 Resolver::doUpgrade( UpgradeStatistics & opt_stats_r )
00253 {
00254 typedef map<PoolItem_Ref,PoolItem_Ref> CandidateMap;
00255 typedef intrusive_ptr<const SplitCap> SplitCapPtr;
00256 typedef map<PoolItem_Ref,PoolItemOrderSet> SplitMap;
00257 typedef map<PoolItem_Ref,PoolItemOrderSet> TodoMap;
00258
00259 CandidateMap candidatemap;
00260
00261 SplitMap splitmap;
00262 TodoMap applyingSplits;
00263 TodoMap addSplitted;
00264 TodoMap addProvided;
00265 TodoMap addMultiProvided;
00266
00267 Target_Ptr target;
00268 try {
00269 target = getZYpp()->target();
00270 }
00271 catch( const Exception & excpt_r) {
00272 ERR << "Huh, no target ?";
00273 ZYPP_CAUGHT(excpt_r);
00274 if (!_testing) return;
00275 MIL << "Running in test mode, continuing without target" << endl;
00276 }
00277 MIL << "target at " << target << endl;
00278
00279 MIL << "doUpgrade start... "
00280 << "(delete_unmaintained:" << (opt_stats_r.delete_unmaintained?"yes":"no") << ")"
00281 << "(silent_downgrades:" << (opt_stats_r.silent_downgrades?"yes":"no") << ")"
00282 << "(keep_installed_patches:" << (opt_stats_r.keep_installed_patches?"yes":"no") << ")"
00283 << endl;
00284
00285 _update_items.clear();
00286 {
00287 UpgradeOptions opts( opt_stats_r );
00288 opt_stats_r = UpgradeStatistics();
00289 (UpgradeOptions&)opt_stats_r = opts;
00290 }
00291
00293
00294
00295
00296
00297
00299 PoolItemOrderSet available;
00300
00301 for ( ResPool::const_iterator it = _pool.begin(); it != _pool.end(); ++it ) {
00302 PoolItem_Ref item = *it;
00303 PoolItem_Ref candidate;
00304 PoolItem_Ref installed;
00305
00306 if ( item.status().isToBeUninstalled() ) {
00307 MIL << "doUpgrade available: SKIP to delete " << item << endl;
00308 ++opt_stats_r.pre_todel;
00309 continue;
00310 }
00311 if ( item.status().isLocked() ) {
00312 MIL << "doUpgrade available: SKIP locked " << item << endl;
00313 if ( item.status().staysInstalled() ) {
00314 ++opt_stats_r.pre_nocand;
00315 }
00316 continue;
00317 }
00318
00319 if ( item.status().staysInstalled() ) {
00320 installed = item;
00321 CandidateMap::const_iterator cand_it = candidatemap.find( installed );
00322 if (cand_it != candidatemap.end()) {
00323 candidate = cand_it->second;
00324 }
00325 else {
00326 candidate = Helper::findUpdateItem( _pool, installed );
00327 }
00328 if (!candidate) {
00329 MIL << "doUpgrade available: SKIP no candidate for " << installed << endl;
00330 ++opt_stats_r.pre_nocand;
00331 continue;
00332 }
00333 MIL << "item " << item << " is installed, candidate is " << candidate << endl;
00334 if (candidate.status().isSeen()) {
00335 candidate.status().setSeen(true);
00336 continue;
00337 }
00338 candidate.status().setSeen(true);
00339 candidatemap[installed] = candidate;
00340 }
00341 else {
00342 if (item.status().isSeen()) {
00343 item.status().setSeen(true);
00344 continue;
00345 }
00346 candidate = item;
00347 candidate.status().setSeen(true);
00348 installed = Helper::findInstalledItem( _pool, candidate );
00349 if (installed) {
00350 if ( installed.status().isLocked() ) {
00351 MIL << "doUpgrade available: SKIP candidate " << candidate << ", locked " << installed << endl;
00352 continue;
00353 }
00354
00355 MIL << "found installed " << installed << " for item " << candidate << endl;
00356 CandidateMap::const_iterator cand_it = candidatemap.find( installed );
00357 if (cand_it == candidatemap.end()
00358 || (cand_it->second->arch().compare( candidate->arch() ) < 0)
00359 || ((cand_it->second->arch().compare( candidate->arch() ) == 0)
00360 && (cand_it->second->edition().compare( candidate->edition() ) < 0) ) )
00361 {
00362 candidatemap[installed] = candidate;
00363 }
00364 }
00365 }
00366
00367 ++opt_stats_r.pre_avcand;
00368 available.insert( candidate );
00369
00370 MIL << "installed " << installed << ", candidate " << candidate << endl;
00371
00372
00373 CapSet caps = candidate->dep (Dep::PROVIDES);
00374 for (CapSet::iterator cit = caps.begin(); cit != caps.end(); ++cit ) {
00375 if (isKind<capability::SplitCap>( *cit ) ) {
00376
00377 capability::CapabilityImpl::SplitInfo splitinfo = capability::CapabilityImpl::getSplitInfo( *cit );
00378
00379 PoolItem splititem = Helper::findInstalledByNameAndKind (_pool, splitinfo.name, ResTraits<zypp::Package>::kind);
00380 MIL << "has split cap " << splitinfo.name << ":" << splitinfo.path << ", splititem:" << splititem << endl;
00381 if (splititem) {
00382 if (target) {
00383 ResObject::constPtr robj = target->whoOwnsFile( splitinfo.path );
00384 if (robj)
00385 MIL << "whoOwnsFile(): " << *robj << endl;
00386 if (robj
00387 && robj->name() == splitinfo.name)
00388 {
00389 MIL << "split matched !" << endl;
00390 splitmap[splititem].insert( candidate );
00391 }
00392 }
00393 }
00394 }
00395 }
00396
00397 }
00398
00399
00400 for ( ResPool::const_iterator it = _pool.begin(); it != _pool.end(); ++it ) {
00401 it->status().setSeen( false );
00402 }
00403
00404 MIL << "doUpgrade: " << opt_stats_r.pre_todel << " packages tagged to delete" << endl;
00405 MIL << "doUpgrade: " << opt_stats_r.pre_nocand << " packages without candidate (foreign, replaced or dropped)" << endl;
00406 MIL << "doUpgrade: " << opt_stats_r.pre_avcand << " packages available for update" << endl;
00407
00408 MIL << "doUpgrade: going to check " << splitmap.size() << " probably splitted packages" << endl;
00409 {
00411
00412
00413
00414
00415
00416
00418
00419 PoolItem_Ref citem;
00420
00421 for ( SplitMap::iterator it = splitmap.begin(); it != splitmap.end(); ++it ) {
00422 applyingSplits[it->first].insert( it->second.begin(), it->second.end() );
00423 _DEBUG(" split count for " << it->first->name() << " now " << applyingSplits[it->first].size());
00424 }
00425 splitmap.clear();
00426 }
00427
00429
00430
00431
00432
00434 MIL << "doUpgrade pass 1..." << endl;
00435
00436 for ( ResPool::const_iterator it = _pool.begin(); it != _pool.end(); ++it ) {
00437
00438 PoolItem_Ref installed(*it);
00439 ResStatus status (installed.status());
00440
00441 if ( ! status.staysInstalled() ) {
00442 continue;
00443 }
00444 ++opt_stats_r.chk_installed_total;
00445
00446 if ( status.transacts() ) {
00447 MIL << "SKIP to delete: " << installed.resolvable() << endl;
00448 ++opt_stats_r.chk_already_todel;
00449 continue;
00450 }
00451
00452 if ( installed.status().isLocked() ) {
00453 MIL << "SKIP taboo: " << installed << endl;
00454 ++opt_stats_r.chk_is_taboo;
00455 _update_items.push_back( installed );
00456 continue;
00457 }
00458
00459 if ( isKind<Patch>(installed.resolvable())
00460 || isKind<Atom>(installed.resolvable())
00461 || isKind<Script>(installed.resolvable())
00462 || isKind<Message>(installed.resolvable()) )
00463 {
00464 if ( ! opt_stats_r.keep_installed_patches )
00465 {
00466 if ( isKind<Patch>(installed.resolvable()) )
00467 MIL << "OUTDATED Patch: " << installed << endl;
00468 installed.status().setToBeUninstalled( ResStatus::APPL_HIGH );
00469 }
00470 else
00471 {
00472 if ( isKind<Patch>(installed.resolvable()) )
00473 MIL << "SKIP Patch: " << installed << endl;
00474 }
00475 continue;
00476 }
00477
00478 CandidateMap::iterator cand_it = candidatemap.find( installed );
00479
00480 bool probably_dropped = false;
00481
00482 MIL << "REPLACEMENT FOR " << installed << endl;
00484
00486 if ( cand_it != candidatemap.end() ) {
00487
00488 PoolItem_Ref candidate (cand_it->second);
00489
00490 if ( ! candidate.status().isToBeInstalled() ) {
00491 int cmp = installed->edition().compare( candidate->edition() );
00492 if ( cmp < 0 ) {
00493 candidate.status().setToBeInstalled( ResStatus::APPL_HIGH );
00494 MIL << " ==> INSTALL (new version): " << candidate << endl;
00495 ++opt_stats_r.chk_to_update;
00496 } else {
00497
00498
00499 if (cmp == 0
00500 || !downgrade_allowed( installed, candidate,
00501 opt_stats_r.silent_downgrades) )
00502 {
00503 MIL << " ==> (keep installed)" << candidate << endl;
00504 ++opt_stats_r.chk_to_keep_installed;
00505 } else {
00506 candidate.status().setToBeInstalled( ResStatus::APPL_HIGH );
00507 MIL << " ==> INSTALL (SuSE version downgrade): " << candidate << endl;
00508 ++opt_stats_r.chk_to_downgrade;
00509 }
00510 }
00511 } else {
00512 MIL << " ==> INSTALL (preselected): " << candidate << endl;
00513 ++opt_stats_r.chk_already_toins;
00514 }
00515
00516 }
00517 else {
00518
00519
00520
00521
00522
00523 Dep dep (Dep::PROVIDES);
00524 CapFactory factory;
00525 Capability installedCap = factory.parse( installed->kind(), installed->name(), Rel::EQ, installed->edition() );
00526
00527 FindProviders info;
00528
00529 invokeOnEach( _pool.byCapabilityIndexBegin( installed->name(), dep ),
00530 _pool.byCapabilityIndexEnd( installed->name(), dep ),
00531 functor::chain( resfilter::ByCaIUninstalled(),
00532 resfilter::ByCapMatch( installedCap ) ) ,
00533 functor::functorRef<bool,CapAndItem>(info) );
00534
00535 int num_providers = info.providers.size();
00536
00537 _DEBUG("lookup " << num_providers << " provides for installed " << installedCap);
00538
00539
00540 PoolItemOrderSet providers;
00541 for (FindMap::const_iterator mapit = info.providers.begin(); mapit != info.providers.end(); ++mapit) {
00542 providers.insert( mapit->second );
00543 }
00544
00545 switch ( info.providers.size() ) {
00546 case 0:
00547 MIL << " ==> (dropped)" << endl;
00548
00549
00550
00551 probably_dropped = true;
00552 break;
00553 case 1:
00554 addProvided[installed] = providers;
00555 MIL << " ==> REPLACED by: " << (*providers.begin()) << endl;
00556
00557
00558 break;
00559 default:
00560 addMultiProvided[installed] = providers;
00561 MIL << " ==> pass 2 (" << providers.size() << " times provided)" << endl;
00562
00563
00564 break;
00565 }
00566
00567 }
00568
00570
00572
00573 TodoMap::iterator sit = applyingSplits.find( installed );
00574 if ( sit != applyingSplits.end() ) {
00575 PoolItemOrderSet & toadd( sit->second );
00576 if ( !toadd.size() ) {
00577 INT << "Empty SplitPkgMap entry for " << installed << endl;
00578 } else {
00579 for ( PoolItemOrderSet::iterator ait = toadd.begin(); ait != toadd.end(); ++ait ) {
00580 PoolItem_Ref split_candidate = *ait;
00581 MIL << " ==> ADD (splitted): " << split_candidate << endl;
00582 if ( probably_dropped
00583 && split_candidate.status().staysUninstalled()
00584 && doesObsoleteItem (split_candidate, installed))
00585 {
00586 probably_dropped = false;
00587 }
00588 }
00589 addSplitted[installed] = toadd;
00590 }
00591
00592 }
00593
00595
00597
00598 if ( probably_dropped ) {
00599 if ( opt_stats_r.delete_unmaintained ) {
00600 installed.status().setToBeUninstalled( ResStatus::APPL_HIGH );
00601 }
00602 ++opt_stats_r.chk_dropped;
00603 _update_items.push_back( installed );
00604 }
00605
00606 }
00607
00609
00610
00611
00613 MIL << "doUpgrade pass 2..." << endl;
00614
00615
00616
00617 for ( TodoMap::iterator it = addProvided.begin(); it != addProvided.end(); ++it ) {
00618
00619 PoolItemOrderSet & tset( it->second );
00620
00621 for ( PoolItemOrderSet::iterator sit = tset.begin(); sit != tset.end(); ++sit ) {
00622 PoolItem_Ref provider (*sit);
00623
00624 if (provider.status().setToBeInstalled( ResStatus::APPL_HIGH )) {
00625 ++opt_stats_r.chk_replaced;
00626 }
00627
00628
00629
00630 if ( doesObsoleteItem (provider, it->first ) ) {
00631 it->first.status().setToBeUninstalled( ResStatus::APPL_HIGH );
00632 }
00633 }
00634
00635 }
00636
00637
00638
00639 for ( TodoMap::iterator it = addSplitted.begin(); it != addSplitted.end(); ++it ) {
00640
00641 PoolItemOrderSet & tset( it->second );
00642 PoolItem_Ref lastItem = PoolItem_Ref();
00643
00644 for ( PoolItemOrderSet::iterator sit = tset.begin(); sit != tset.end(); ++sit ) {
00645 if (!lastItem
00646 || compareByN ( lastItem.resolvable(), sit->resolvable()) != 0)
00647 {
00648 PoolItem_Ref item( *sit );
00649
00650
00651
00652 PoolItem_Ref already_installed = Helper::findInstalledItem( _pool, item );
00653 if (!already_installed
00654 || already_installed->edition().compare( item->edition() ) != 0 )
00655 {
00656 if (item.status().setToBeInstalled( ResStatus::APPL_HIGH )) {
00657 ++opt_stats_r.chk_add_split;
00658 }
00659 }
00660 }
00661 lastItem = *sit;
00662 }
00663
00664 }
00665
00666
00667
00668 for ( TodoMap::iterator it = addMultiProvided.begin(); it != addMultiProvided.end(); ++it ) {
00669 MIL << "GET ONE OUT OF " << it->second.size() << " for " << it->first << endl;
00670
00671 PoolItem_Ref guess;
00672 PoolItemOrderSet & gset( it->second );
00673
00674 for ( PoolItemOrderSet::iterator git = gset.begin(); git != gset.end(); ++git ) {
00675 PoolItem_Ref item (*git);
00676
00677 if (git == gset.begin())
00678 guess = item;
00679
00680 if ( item.status().isToBeInstalled()) {
00681 MIL << " ==> (pass 2: meanwhile set to install): " << item << endl;
00682 if ( ! doesObsoleteItem (item, it->first ) ) {
00683 it->first.status().setToBeUninstalled( ResStatus::APPL_HIGH );
00684 }
00685 guess = PoolItem_Ref();
00686 break;
00687 } else {
00688
00689
00690
00691
00692
00693
00694 if ( !guess || guess->name().size() > item->name().size() ) {
00695 guess = item;
00696 }
00697 }
00698 }
00699
00700 if ( guess ) {
00701 guess.status().setToBeInstalled( ResStatus::APPL_HIGH );
00702 MIL << " ==> REPLACED by: (pass 2: guessed): " << guess << endl;
00703 if ( ! doesObsoleteItem (guess, it->first ) ) {
00704 it->first.status().setToBeUninstalled( ResStatus::APPL_HIGH );
00705 }
00706 ++opt_stats_r.chk_replaced_guessed;
00707 }
00708 }
00709
00711
00713 MIL << opt_stats_r << endl;
00714
00715
00716 _upgradeMode = true;
00717 }
00718
00720 };
00723 };
00726 };
00728
00729