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, Target_Ptr target)
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 if (ipkg) DBG << "Installed vendor '" << ipkg->vendor() << "'" << endl;
00131 if (cpkg) DBG << "Candidate vendor '" << cpkg->vendor() << "'" << endl;
00132 if (cpkg
00133 && VendorAttr::instance().isKnown( cpkg->vendor() ) )
00134 {
00135 if (target
00136 && target->root().asString() != "/")
00137 {
00138 return true;
00139 }
00140 if ( ipkg->buildtime() < cpkg->buildtime() ) {
00141 MIL << "allowed downgrade " << installed << " to " << candidate << endl;
00142 return true;
00143 }
00144 }
00145 return false;
00146 }
00147
00148
00149
00150 struct FindObsoletes
00151 {
00152 bool obsoletes;
00153
00154 FindObsoletes ()
00155 : obsoletes (false)
00156 { }
00157
00158 bool operator()( const CapAndItem & cai )
00159 {
00160 obsoletes = true;
00161 return false;
00162 }
00163 };
00164
00165
00166
00167
00168 bool
00169 Resolver::doesObsoleteCapability (PoolItem_Ref candidate, const Capability & cap)
00170 {
00171 _DEBUG("doesObsoleteCapability " << candidate << ", " << cap);
00172
00173 Dep dep (Dep::OBSOLETES);
00174 FindObsoletes info;
00175 invokeOnEach( _pool.byCapabilityIndexBegin( cap.index(), dep ),
00176 _pool.byCapabilityIndexEnd( cap.index(), dep ),
00177 resfilter::ByCapMatch( cap ),
00178 functor::functorRef<bool,CapAndItem>(info) );
00179
00180 _DEBUG((info.obsoletes ? "YES" : "NO"));
00181 return info.obsoletes;
00182 }
00183
00184
00185 bool
00186 Resolver::doesObsoleteItem (PoolItem_Ref candidate, PoolItem_Ref installed)
00187 {
00188 CapFactory factory;
00189 Capability installedCap = factory.parse ( installed->kind(), installed->name(), Rel::EQ, installed->edition());
00190
00191 return doesObsoleteCapability (candidate, installedCap);
00192 }
00193
00194
00195
00196
00197
00198
00199
00200 typedef map<string, PoolItem_Ref> FindMap;
00201
00202 struct FindProviders
00203 {
00204 FindMap providers;
00205
00206 FindProviders ()
00207 { }
00208
00209 bool operator()( const CapAndItem & cai )
00210 {
00211 PoolItem provider( cai.item );
00212 if ( provider.status().isToBeUninstalled() ) {
00213 MIL << " IGNORE relation match (package is tagged to delete): " << cai.cap << " ==> " << provider << endl;
00214 }
00215 else {
00216 FindMap::iterator it = providers.find( provider->name() );
00217
00218 if (it != providers.end()) {
00219 int cmp = it->second->arch().compare( provider->arch() );
00220 if (cmp < 0) {
00221 it->second = provider;
00222 }
00223 else if (cmp == 0) {
00224 if (it->second->edition().compare( provider->edition() ) < 0) {
00225 it->second = provider;
00226 }
00227 }
00228 }
00229 else {
00230 providers[provider->name()] = provider;
00231 }
00232 }
00233 return true;
00234 }
00235 };
00236
00237
00238
00239
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250 void
00251 Resolver::doUpgrade( UpgradeStatistics & opt_stats_r )
00252 {
00253 typedef map<PoolItem_Ref,PoolItem_Ref> CandidateMap;
00254 typedef intrusive_ptr<const SplitCap> SplitCapPtr;
00255 typedef map<PoolItem_Ref,PoolItemOrderSet> SplitMap;
00256 typedef map<PoolItem_Ref,PoolItemOrderSet> TodoMap;
00257
00258 CandidateMap candidatemap;
00259
00260 SplitMap splitmap;
00261 TodoMap applyingSplits;
00262 TodoMap addSplitted;
00263 TodoMap addProvided;
00264 TodoMap addMultiProvided;
00265
00266 Target_Ptr target;
00267 try {
00268 target = getZYpp()->target();
00269 }
00270 catch( const Exception & excpt_r) {
00271 ERR << "Huh, no target ?";
00272 ZYPP_CAUGHT(excpt_r);
00273 if (!_testing) return;
00274 MIL << "Running in test mode, continuing without target" << endl;
00275 }
00276 MIL << "target at " << target << endl;
00277
00278 MIL << "doUpgrade start... "
00279 << "(delete_unmaintained:" << (opt_stats_r.delete_unmaintained?"yes":"no") << ")"
00280 << endl;
00281
00282 _update_items.clear();
00283 {
00284 UpgradeOptions opts( opt_stats_r );
00285 opt_stats_r = UpgradeStatistics();
00286 (UpgradeOptions&)opt_stats_r = opts;
00287 }
00288
00290
00291
00292
00293
00294
00296 PoolItemOrderSet available;
00297
00298 for ( ResPool::const_iterator it = _pool.begin(); it != _pool.end(); ++it ) {
00299 PoolItem_Ref item = *it;
00300 PoolItem_Ref candidate;
00301 PoolItem_Ref installed;
00302
00303 if ( item.status().isToBeUninstalled() ) {
00304 MIL << "doUpgrade available: SKIP to delete " << item << endl;
00305 ++opt_stats_r.pre_todel;
00306 continue;
00307 }
00308 if ( item.status().isLocked() ) {
00309 MIL << "doUpgrade available: SKIP locked " << item << endl;
00310 if ( item.status().staysInstalled() ) {
00311 ++opt_stats_r.pre_nocand;
00312 }
00313 continue;
00314 }
00315
00316 if ( item.status().staysInstalled() ) {
00317 installed = item;
00318 CandidateMap::const_iterator cand_it = candidatemap.find( installed );
00319 if (cand_it != candidatemap.end()) {
00320 candidate = cand_it->second;
00321 }
00322 else {
00323 candidate = Helper::findUpdateItem( _pool, installed );
00324 }
00325 if (!candidate) {
00326 MIL << "doUpgrade available: SKIP no candidate for " << installed << endl;
00327 ++opt_stats_r.pre_nocand;
00328 continue;
00329 }
00330 MIL << "item " << item << " is installed, candidate is " << candidate << endl;
00331 if (candidate.status().isSeen()) {
00332 candidate.status().setSeen(true);
00333 continue;
00334 }
00335 candidate.status().setSeen(true);
00336 candidatemap[installed] = candidate;
00337 }
00338 else {
00339 if (item.status().isSeen()) {
00340 item.status().setSeen(true);
00341 continue;
00342 }
00343 candidate = item;
00344 candidate.status().setSeen(true);
00345 installed = Helper::findInstalledItem( _pool, candidate );
00346 if (installed) {
00347 if ( installed.status().isLocked() ) {
00348 MIL << "doUpgrade available: SKIP candidate " << candidate << ", locked " << installed << endl;
00349 continue;
00350 }
00351
00352 MIL << "found installed " << installed << " for item " << candidate << endl;
00353 CandidateMap::const_iterator cand_it = candidatemap.find( installed );
00354 if (cand_it == candidatemap.end()
00355 || (cand_it->second->arch().compare( candidate->arch() ) < 0)
00356 || ((cand_it->second->arch().compare( candidate->arch() ) == 0)
00357 && (cand_it->second->edition().compare( candidate->edition() ) < 0) ) )
00358 {
00359 candidatemap[installed] = candidate;
00360 }
00361 }
00362 }
00363
00364 ++opt_stats_r.pre_avcand;
00365 available.insert( candidate );
00366
00367 MIL << "installed " << installed << ", candidate " << candidate << endl;
00368
00369
00370 CapSet caps = candidate->dep (Dep::PROVIDES);
00371 for (CapSet::iterator cit = caps.begin(); cit != caps.end(); ++cit ) {
00372 if (isKind<capability::SplitCap>( *cit ) ) {
00373
00374 capability::CapabilityImpl::SplitInfo splitinfo = capability::CapabilityImpl::getSplitInfo( *cit );
00375
00376 PoolItem splititem = Helper::findInstalledByNameAndKind (_pool, splitinfo.name, ResTraits<zypp::Package>::kind);
00377 MIL << "has split cap " << splitinfo.name << ":" << splitinfo.path << ", splititem:" << splititem << endl;
00378 if (splititem) {
00379 if (target) {
00380 ResObject::constPtr robj = target->whoOwnsFile( splitinfo.path );
00381 if (robj) MIL << "whoOwnsFile(): " << *robj << endl;
00382 if (robj
00383 && robj->name() == splitinfo.name)
00384 {
00385 MIL << "split matched !" << endl;
00386 splitmap[splititem].insert( candidate );
00387 }
00388 }
00389 }
00390 }
00391 }
00392
00393 }
00394
00395
00396 for ( ResPool::const_iterator it = _pool.begin(); it != _pool.end(); ++it ) {
00397 it->status().setSeen( false );
00398 }
00399
00400 #warning Cant update from broken install medium like STABLE
00401 #if 0
00402
00403
00404
00405 {
00406 CheckSetDeps::BrokenMap broken;
00407 CheckSetDeps checker(available, broken);
00408
00409 checker.setTrackRelations(false);
00410 checker.checkAll();
00411
00412 if(!broken.empty())
00413 {
00414 CheckSetDeps::BrokenMap::iterator bit, bend;
00415 for(bit = broken.begin(), bend = broken.end(); bit != bend; ++bit)
00416 {
00417 MIL << bit->first->name() << " is broken, not considering it for update" << endl;
00418 available.remove(bit->first);
00419 --opt_stats_r.pre_avcand;
00420 ++opt_stats_r.pre_nocand;
00421 }
00422 }
00423 }
00424 #endif
00425
00426 MIL << "doUpgrade: " << opt_stats_r.pre_todel << " packages tagged to delete" << endl;
00427 MIL << "doUpgrade: " << opt_stats_r.pre_nocand << " packages without candidate (foreign, replaced or dropped)" << endl;
00428 MIL << "doUpgrade: " << opt_stats_r.pre_avcand << " packages available for update" << endl;
00429
00430 MIL << "doUpgrade: going to check " << splitmap.size() << " probably splitted packages" << endl;
00431 {
00433
00434
00435
00436
00437
00438
00440
00441 PoolItem_Ref citem;
00442
00443 for ( SplitMap::iterator it = splitmap.begin(); it != splitmap.end(); ++it ) {
00444 applyingSplits[it->first].insert( it->second.begin(), it->second.end() );
00445 _DEBUG(" split count for " << it->first->name() << " now " << applyingSplits[it->first].size());
00446 }
00447 splitmap.clear();
00448 }
00449
00451
00452
00453
00454
00456 MIL << "doUpgrade pass 1..." << endl;
00457
00458 for ( ResPool::const_iterator it = _pool.begin(); it != _pool.end(); ++it ) {
00459
00460 PoolItem_Ref installed(*it);
00461 ResStatus status (installed.status());
00462
00463 if ( ! status.staysInstalled() ) {
00464 continue;
00465 }
00466 ++opt_stats_r.chk_installed_total;
00467
00468 if ( status.transacts() ) {
00469 MIL << "SKIP to delete: " << installed.resolvable() << endl;
00470 ++opt_stats_r.chk_already_todel;
00471 continue;
00472 }
00473
00474 if ( installed.status().isLocked() ) {
00475 MIL << "SKIP taboo: " << installed << endl;
00476 ++opt_stats_r.chk_is_taboo;
00477 _update_items.push_back( installed );
00478 continue;
00479 }
00480
00481 CandidateMap::iterator cand_it = candidatemap.find( installed );
00482
00483 bool probably_dropped = false;
00484
00485 MIL << "REPLACEMENT FOR " << installed << endl;
00487
00489 if ( cand_it != candidatemap.end() ) {
00490
00491 PoolItem_Ref candidate (cand_it->second);
00492
00493 if ( ! candidate.status().isToBeInstalled() ) {
00494 int cmp = installed->edition().compare( candidate->edition() );
00495 if ( cmp < 0 ) {
00496 candidate.status().setToBeInstalled( ResStatus::APPL_HIGH );
00497 MIL << " ==> INSTALL (new version): " << candidate << endl;
00498 ++opt_stats_r.chk_to_update;
00499 } else {
00500
00501
00502 if (cmp == 0
00503 || !downgrade_allowed (installed, candidate, target))
00504 {
00505 MIL << " ==> (keep installed)" << candidate << endl;
00506 ++opt_stats_r.chk_to_keep_installed;
00507 } else {
00508 candidate.status().setToBeInstalled( ResStatus::APPL_HIGH );
00509 MIL << " ==> INSTALL (SuSE version downgrade): " << candidate << endl;
00510 ++opt_stats_r.chk_to_downgrade;
00511 }
00512 }
00513 } else {
00514 MIL << " ==> INSTALL (preselected): " << candidate << endl;
00515 ++opt_stats_r.chk_already_toins;
00516 }
00517
00518 }
00519 else {
00520
00521
00522
00523
00524
00525 Dep dep (Dep::PROVIDES);
00526 CapFactory factory;
00527 Capability installedCap = factory.parse( installed->kind(), installed->name(), Rel::EQ, installed->edition() );
00528
00529 FindProviders info;
00530
00531 invokeOnEach( _pool.byCapabilityIndexBegin( installed->name(), dep ),
00532 _pool.byCapabilityIndexEnd( installed->name(), dep ),
00533 functor::chain( resfilter::ByCaIUninstalled(),
00534 resfilter::ByCapMatch( installedCap ) ) ,
00535 functor::functorRef<bool,CapAndItem>(info) );
00536
00537 int num_providers = info.providers.size();
00538
00539 _DEBUG("lookup " << num_providers << " provides for installed " << installedCap);
00540
00541
00542 PoolItemOrderSet providers;
00543 for (FindMap::const_iterator mapit = info.providers.begin(); mapit != info.providers.end(); ++mapit) {
00544 providers.insert( mapit->second );
00545 }
00546
00547 switch ( info.providers.size() ) {
00548 case 0:
00549 MIL << " ==> (dropped)" << endl;
00550
00551
00552
00553 probably_dropped = true;
00554 break;
00555 case 1:
00556 addProvided[installed] = providers;
00557 MIL << " ==> REPLACED by: " << (*providers.begin()) << endl;
00558
00559
00560 break;
00561 default:
00562 addMultiProvided[installed] = providers;
00563 MIL << " ==> pass 2 (" << providers.size() << " times provided)" << endl;
00564
00565
00566 break;
00567 }
00568
00569 }
00570
00572
00574
00575 TodoMap::iterator sit = applyingSplits.find( installed );
00576 if ( sit != applyingSplits.end() ) {
00577 PoolItemOrderSet & toadd( sit->second );
00578 if ( !toadd.size() ) {
00579 INT << "Empty SplitPkgMap entry for " << installed << endl;
00580 } else {
00581 for ( PoolItemOrderSet::iterator ait = toadd.begin(); ait != toadd.end(); ++ait ) {
00582 PoolItem_Ref split_candidate = *ait;
00583 MIL << " ==> ADD (splitted): " << split_candidate << endl;
00584 if ( probably_dropped
00585 && split_candidate.status().staysUninstalled()
00586 && doesObsoleteItem (split_candidate, installed))
00587 {
00588 probably_dropped = false;
00589 }
00590 }
00591 addSplitted[installed] = toadd;
00592 }
00593
00594 }
00595
00597
00599
00600 if ( probably_dropped ) {
00601 if ( opt_stats_r.delete_unmaintained ) {
00602 installed.status().setToBeUninstalled( ResStatus::APPL_HIGH );
00603 }
00604 ++opt_stats_r.chk_dropped;
00605 _update_items.push_back( installed );
00606 }
00607
00608 }
00609
00611
00612
00613
00615 MIL << "doUpgrade pass 2..." << endl;
00616
00617
00618
00619 for ( TodoMap::iterator it = addProvided.begin(); it != addProvided.end(); ++it ) {
00620
00621 PoolItemOrderSet & tset( it->second );
00622
00623 for ( PoolItemOrderSet::iterator sit = tset.begin(); sit != tset.end(); ++sit ) {
00624 PoolItem_Ref provider (*sit);
00625
00626 if (provider.status().setToBeInstalled( ResStatus::APPL_HIGH )) {
00627 ++opt_stats_r.chk_replaced;
00628 }
00629
00630
00631
00632 if ( doesObsoleteItem (provider, it->first ) ) {
00633 it->first.status().setToBeUninstalled( ResStatus::APPL_HIGH );
00634 }
00635 }
00636
00637 }
00638
00639
00640
00641 for ( TodoMap::iterator it = addSplitted.begin(); it != addSplitted.end(); ++it ) {
00642
00643 PoolItemOrderSet & tset( it->second );
00644 PoolItem_Ref lastItem = PoolItem_Ref();
00645
00646 for ( PoolItemOrderSet::iterator sit = tset.begin(); sit != tset.end(); ++sit ) {
00647 if (!lastItem
00648 || compareByN ( lastItem.resolvable(), sit->resolvable()) != 0)
00649 {
00650 PoolItem_Ref item( *sit );
00651
00652
00653
00654 PoolItem_Ref already_installed = Helper::findInstalledItem( _pool, item );
00655 if (!already_installed
00656 || already_installed->edition().compare( item->edition() ) != 0 )
00657 {
00658 if (item.status().setToBeInstalled( ResStatus::APPL_HIGH )) {
00659 ++opt_stats_r.chk_add_split;
00660 }
00661 }
00662 }
00663 lastItem = *sit;
00664 }
00665
00666 }
00667
00668
00669
00670 for ( TodoMap::iterator it = addMultiProvided.begin(); it != addMultiProvided.end(); ++it ) {
00671 MIL << "GET ONE OUT OF " << it->second.size() << " for " << it->first << endl;
00672
00673 PoolItem_Ref guess;
00674 PoolItemOrderSet & gset( it->second );
00675
00676 for ( PoolItemOrderSet::iterator git = gset.begin(); git != gset.end(); ++git ) {
00677 PoolItem_Ref item (*git);
00678
00679 if (git == gset.begin())
00680 guess = item;
00681
00682 if ( item.status().isToBeInstalled()) {
00683 MIL << " ==> (pass 2: meanwhile set to install): " << item << endl;
00684 if ( ! doesObsoleteItem (item, it->first ) ) {
00685 it->first.status().setToBeUninstalled( ResStatus::APPL_HIGH );
00686 }
00687 guess = PoolItem_Ref();
00688 break;
00689 } else {
00690
00691
00692
00693
00694
00695
00696 if ( !guess || guess->name().size() > item->name().size() ) {
00697 guess = item;
00698 }
00699 }
00700 }
00701
00702 if ( guess ) {
00703 guess.status().setToBeInstalled( ResStatus::APPL_HIGH );
00704 MIL << " ==> REPLACED by: (pass 2: guessed): " << guess << endl;
00705 if ( ! doesObsoleteItem (guess, it->first ) ) {
00706 it->first.status().setToBeUninstalled( ResStatus::APPL_HIGH );
00707 }
00708 ++opt_stats_r.chk_replaced_guessed;
00709 }
00710 }
00711
00713
00715 MIL << opt_stats_r << endl;
00716
00717
00718 _upgradeMode = true;
00719 }
00720
00722 };
00725 };
00728 };
00730
00731