1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /*---------------------------------------------------------------------\
4 | |__ / \ / / . \ . \ |
9 \---------------------------------------------------------------------*/
12 * Implements the distribution upgrade algorithm.
14 * Copyright (C) 2005 SUSE Linux Products GmbH
16 * This program is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU General Public License,
18 * version 2, as published by the Free Software Foundation.
20 * This program is distributed in the hope that it will be useful, but
21 * WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * General Public License for more details.
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
32 stolen from PMPackageManager_update.cc
33 original author Michael Andres <ma@suse.de>
34 zypp port by Klaus Kaempf <kkaempf@suse.de>
38 #include "zypp/Capabilities.h"
39 #include "zypp/base/Easy.h"
40 #include "zypp/base/LogTools.h"
41 #include "zypp/base/String.h"
42 #include "zypp/base/Gettext.h"
43 #include "zypp/base/Exception.h"
44 #include "zypp/VendorAttr.h"
45 #include "zypp/base/Algorithm.h"
46 #include "zypp/ResPool.h"
47 #include "zypp/ResStatus.h"
48 #include "zypp/ResFilters.h"
49 #include "zypp/CapFilters.h"
50 #include "zypp/Capability.h"
51 #include "zypp/VendorAttr.h"
52 #include "zypp/Package.h"
53 #include "zypp/ZYppFactory.h"
54 #include "zypp/solver/detail/Types.h"
55 #include "zypp/solver/detail/Helper.h"
56 #include "zypp/solver/detail/Resolver.h"
57 #include "zypp/solver/detail/Testcase.h"
58 #include "zypp/Target.h"
59 #include "zypp/sat/SATResolver.h"
61 /////////////////////////////////////////////////////////////////////////
63 { ///////////////////////////////////////////////////////////////////////
64 ///////////////////////////////////////////////////////////////////////
66 { /////////////////////////////////////////////////////////////////////
67 /////////////////////////////////////////////////////////////////////
69 { ///////////////////////////////////////////////////////////////////
74 /** Order on AvialableItemSet.
77 * \li ResObject::constPtr as fallback.
79 struct AVOrder : public std::binary_function<PoolItem,PoolItem,bool>
81 // NOTE: operator() provides LESS semantics to order the set.
82 // So LESS means 'prior in set'. We want 'better' archs and
83 // 'better' editions at the beginning of the set. So we return
84 // TRUE if (lhs > rhs)!
86 bool operator()( const PoolItem lhs, const PoolItem rhs ) const
88 int res = lhs->arch().compare( rhs->arch() );
91 res = lhs->edition().compare( rhs->edition() );
95 // no more criteria, still equal:
96 // use the ResObject::constPtr (the poiner value)
97 // (here it's arbitrary whether < or > )
98 return lhs.resolvable() < rhs.resolvable();
102 typedef std::set<PoolItem, AVOrder> PoolItemOrderSet;
106 // check if downgrade is allowed
107 // (Invariant on entry: installed.edition >= candidate.edition)
109 // candidate must have allowed vendor (e.g. 'SuSE', 'Novell', ...) and candidates buildtime must be
113 downgrade_allowed( PoolItem installed, PoolItem candidate, bool silent_downgrades )
115 if (installed.status().isLocked()) {
116 MIL << "Installed " << installed << " is locked, not upgrading" << endl;
120 Resolvable::constPtr ires = installed.resolvable();
121 Package::constPtr ipkg = asKind<Package>(ires);
122 Resolvable::constPtr cres = candidate.resolvable();
123 Package::constPtr cpkg = asKind<Package>(cres);
126 DBG << "Installed vendor '" << ipkg->vendor() << "'" << endl;
128 DBG << "Candidate vendor '" << cpkg->vendor() << "'" << endl;
131 && VendorAttr::instance().equivalent( ipkg->vendor(), cpkg->vendor() ) )
133 if ( silent_downgrades )
135 if ( ipkg->buildtime() < cpkg->buildtime() ) { // installed has older buildtime
136 MIL << "allowed downgrade " << installed << " to " << candidate << endl;
137 return true; // see bug #152760
145 Resolver::doesObsoleteItem (PoolItem candidate, PoolItem installed)
147 return _satResolver->doesObsoleteItem (candidate, installed);
150 //-----------------------------------------------------------------------------
153 //-----------------------------------------------------------------------------
155 // Selecting item for installation
157 class LookForSelected : public resfilter::PoolItemFilterFunctor
163 LookForSelected (PoolItem can)
168 bool operator()( PoolItem item )
170 if (item.status().isToBeInstalled()
171 && item->edition() == candidate->edition()
172 && item->arch() == candidate->arch()) {
173 MIL << item << " is already selected for installation --> ignoring" << endl;
175 return false; // stop here
181 bool setForInstallation (const ResPool &pool, PoolItem item) {
182 LookForSelected info(item);
184 invokeOnEach( pool.byIdentBegin (item->kind(),item->name()),
185 pool.byIdentEnd (item->kind(),item->name()),
186 resfilter::ByUninstalled (), // ByUninstalled
187 functor::functorRef<bool,PoolItem> (info) );
189 MIL << " ---> " << item << " will be ignoring" << endl;
192 return item.status().setToBeInstalled( ResStatus::APPL_HIGH );
196 //-----------------------------------------------------------------------------
198 ///////////////////////////////////////////////////////////////////
201 // METHOD NAME : Resolver::doUpgrade
204 // DESCRIPTION : go through all installed (but not yet touched by user)
205 // packages and look for update candidates
208 Resolver::doUpgrade( UpgradeStatistics & opt_stats_r )
210 typedef map<PoolItem,PoolItem> CandidateMap;
211 typedef map<PoolItem,PoolItemOrderSet> TodoMap;
213 CandidateMap candidatemap;
216 TodoMap addMultiProvided;
218 sat::Pool::instance().prepare();
222 target = getZYpp()->target();
224 catch( const Exception & excpt_r) {
225 ERR << "Huh, no target ?";
226 ZYPP_CAUGHT(excpt_r);
227 if (!_testing) return; // can't continue without target
228 MIL << "Running in test mode, continuing without target" << endl;
230 MIL << "target at " << target << endl;
232 MIL << "doUpgrade start... "
233 << "(delete_unmaintained:" << (opt_stats_r.delete_unmaintained?"yes":"no") << ")"
234 << "(silent_downgrades:" << (opt_stats_r.silent_downgrades?"yes":"no") << ")"
235 << "(keep_installed_patches:" << (opt_stats_r.keep_installed_patches?"yes":"no") << ")"
238 // create a testcase for the updating system
239 Testcase testcase("/var/log/updateTestcase");
240 testcase.createTestcase (*this, true, false); // create pool, do not solve
242 _update_items.clear();
244 UpgradeOptions opts( opt_stats_r );
245 opt_stats_r = UpgradeStatistics();
246 (UpgradeOptions&)opt_stats_r = opts;
249 /* Find upgrade candidates for each package. */
251 for ( ResPool::const_iterator it = _pool.begin(); it != _pool.end(); ++it ) {
256 if ( item.status().isToBeUninstalled() ) {
257 MIL << "doUpgrade available: SKIP to delete " << item << endl;
258 ++opt_stats_r.pre_todel;
261 if ( item.status().isLocked() ) {
262 MIL << "doUpgrade available: SKIP locked " << item << endl;
263 if ( item.status().staysInstalled() ) {
264 ++opt_stats_r.pre_nocand;
269 if ( item.status().staysInstalled() ) { // installed item
271 CandidateMap::const_iterator cand_it = candidatemap.find( installed );
272 if (cand_it != candidatemap.end()) {
273 candidate = cand_it->second; // found candidate already
276 candidate = Helper::findUpdateItem( _pool, installed ); // find 'best' upgrade candidate
279 MIL << "doUpgrade available: SKIP no candidate for " << installed << endl;
280 ++opt_stats_r.pre_nocand;
283 if (candidate.status().isSeen()) { // seen already
284 candidate.status().setSeen(true);
287 candidate.status().setSeen(true); // mark as seen
288 candidatemap[installed] = candidate;
290 else { // assume Uninstalled
291 if (item.status().isSeen()) { // seen already
292 item.status().setSeen(true);
296 candidate.status().setSeen(true); // mark as seen
297 installed = Helper::findInstalledItem( _pool, candidate );
298 if (installed) { // check if we already have an installed
299 if ( installed.status().isLocked() ) {
300 MIL << "doUpgrade available: SKIP candidate " << candidate << ", locked " << installed << endl;
304 if ( !VendorAttr::instance().equivalent(installed->vendor(), candidate->vendor()) )
306 MIL << "Discarding '" << candidate << "' from vendor '"
307 << candidate->vendor() << "' different to installed '"
308 << installed->vendor() << "' vendor." << endl;
312 CandidateMap::const_iterator cand_it = candidatemap.find( installed );
313 if (cand_it == candidatemap.end() // not in map yet
314 || (cand_it->second->arch().compare( candidate->arch() ) < 0) // or the new has better architecture
315 || ((cand_it->second->arch().compare( candidate->arch() ) == 0) // or the new has the same architecture
316 && (cand_it->second->edition().compare( candidate->edition() ) < 0)) // and a better edition (-> 157501)
319 candidatemap[installed] = candidate; // put it in !
324 ++opt_stats_r.pre_avcand;
325 } // iterate over the complete pool
327 // reset all seen (for next run)
328 for ( ResPool::const_iterator it = _pool.begin(); it != _pool.end(); ++it ) {
329 it->status().setSeen( false );
332 MIL << "doUpgrade: " << opt_stats_r.pre_todel << " packages tagged to delete" << endl;
333 MIL << "doUpgrade: " << opt_stats_r.pre_nocand << " packages without candidate (foreign, replaced or dropped)" << endl;
334 MIL << "doUpgrade: " << opt_stats_r.pre_avcand << " packages available for update" << endl;
336 ///////////////////////////////////////////////////////////////////
337 // Now iterate installed packages, not selected to delete, and
338 // figure out what might be an appropriate replacement. Current
339 // packages state is changed immediately. Additional packages are
340 // reported but set to install later.
341 ///////////////////////////////////////////////////////////////////
342 MIL << "doUpgrade pass 1..." << endl;
344 for ( ResPool::const_iterator it = _pool.begin(); it != _pool.end(); ++it ) {
346 PoolItem installed(*it);
347 ResStatus status (installed.status());
349 if ( ! status.staysInstalled() ) {
352 ++opt_stats_r.chk_installed_total;
354 if ( status.transacts() ) { // we know its installed, if it transacts also
355 MIL << "SKIP to delete: " << installed.resolvable() << endl; // it'll be deleted
356 ++opt_stats_r.chk_already_todel;
360 if ( installed.status().isLocked() ) { // skip locked
361 MIL << "SKIP taboo: " << installed << endl;
362 ++opt_stats_r.chk_is_taboo;
363 _update_items.push_back( installed ); // remember in problem list
367 if ( isKind<Patch>(installed.resolvable())
368 || isKind<Atom>(installed.resolvable())
369 || isKind<Script>(installed.resolvable())
370 || isKind<Message>(installed.resolvable()) )
372 if ( ! opt_stats_r.keep_installed_patches )
374 if ( isKind<Patch>(installed.resolvable()) )
375 MIL << "OUTDATED Patch: " << installed << endl;
376 installed.status().setToBeUninstalled( ResStatus::APPL_HIGH );
380 if ( isKind<Patch>(installed.resolvable()) )
381 MIL << "SKIP Patch: " << installed << endl;
386 CandidateMap::iterator cand_it = candidatemap.find( installed );
388 bool probably_dropped = false;
390 MIL << "REPLACEMENT FOR " << installed << endl;
391 ///////////////////////////////////////////////////////////////////
392 // figure out replacement
393 ///////////////////////////////////////////////////////////////////
394 if ( cand_it != candidatemap.end() ) {
396 PoolItem candidate (cand_it->second);
398 if ( ! candidate.status().isToBeInstalled() ) {
399 int cmp = installed->edition().compare( candidate->edition() );
400 if ( cmp < 0 ) { // new edition
401 setForInstallation (_pool,candidate);
402 MIL << " ==> INSTALL (new version): " << candidate << endl;
403 ++opt_stats_r.chk_to_update;
404 } else { // older or equal edition
405 // check whether to downgrade:
407 if (cmp == 0 // equal
408 || !downgrade_allowed( installed, candidate,
409 opt_stats_r.silent_downgrades) ) // or downgrade not allowed
411 MIL << " ==> (keep installed)" << candidate << endl; // keep installed
412 ++opt_stats_r.chk_to_keep_installed;
413 } else {// older and downgrade allowed
414 setForInstallation (_pool, candidate);
415 MIL << " ==> INSTALL (SuSE version downgrade): " << candidate << endl;
416 ++opt_stats_r.chk_to_downgrade;
420 MIL << " ==> INSTALL (preselected): " << candidate << endl;
421 ++opt_stats_r.chk_already_toins;
425 else { // no candidate
427 // replaced or dropped (anyway there's no candidate for this!)
428 // If unique provides exists check if obsoleted (replaced).
429 // Remember new package for 2nd pass.
431 Capability installedCap( installed->name(), Rel::EQ, installed->edition(), installed->kind());
432 // find ALL providers
433 PoolItemList possibleProviders = _satResolver->whoProvides (installedCap);
435 // find best available providers for installed name
436 typedef map<string, PoolItem> FindMap;
437 FindMap providersMap; // the best providers which matched
438 bool otherVendorFound = false;
439 for (PoolItemList::const_iterator iter = possibleProviders.begin(); iter != possibleProviders.end(); iter++) {
440 PoolItem provider = *iter;
441 if ( !VendorAttr::instance().equivalent(provider->vendor(), installed->vendor()) )
443 MIL << "Discarding '" << provider << "' from vendor '"
444 << provider->vendor() << "' different to uninstalled '"
445 << installed->vendor() << "' vendor." << endl;
446 otherVendorFound = true;
447 } else if ( provider.status().isToBeUninstalled() ) {
448 MIL << " IGNORE relation match (package is tagged to delete): " << provider << endl;
451 FindMap::iterator it = providersMap.find( provider->name() );
453 if (it != providersMap.end()) { // provider with same name found
454 if (provider.status().isToBeInstalled()
455 || it->second.status().isToBeInstalled()) {
457 if (provider.status().isToBeInstalled()
458 && it->second.status().isToBeInstalled()) {
459 ERR << "only one should be set for installation: " << it->second << "; " << provider << endl;
461 if (provider.status().isToBeInstalled()) {
462 it->second = provider; // take thatone which is already set for installation
466 // not the same --> find better provider
467 int cmp = it->second->arch().compare( provider->arch() );
468 if (cmp < 0) { // new provider has better arch
469 it->second = provider;
471 else if (cmp == 0) { // new provider has equal arch
472 if (it->second->edition().compare( provider->edition() ) < 0) {
473 it->second = provider; // new provider has better edition
479 providersMap[provider->name()] = provider;
484 _DEBUG("lookup " << providersMap.size() << " provides for installed " << installedCap);
486 // copy from map to set
487 PoolItemOrderSet providers;
488 for (FindMap::const_iterator mapit = providersMap.begin(); mapit != providersMap.end(); ++mapit) {
489 providers.insert( mapit->second );
492 switch ( providersMap.size() ) {
494 if (otherVendorFound) {
495 MIL << " only resolvable with other vendor found ==> do nothing" << endl;
497 MIL << " ==> (dropped)" << endl;
498 probably_dropped = true;
502 addProvided[installed] = providers;
503 MIL << " ==> REPLACED by: " << (*providers.begin()) << endl;
505 // check obsoletes later
508 addMultiProvided[installed] = providers;
509 MIL << " ==> pass 2 (" << providers.size() << " times provided)" << endl;
511 // check obsoletes later
518 ///////////////////////////////////////////////////////////////////
519 // now handle dropped package
520 ///////////////////////////////////////////////////////////////////
522 if ( probably_dropped ) {
523 if ( opt_stats_r.delete_unmaintained
524 && VendorAttr::instance().equivalent( installed->vendor(), "suse" ) ) {
525 installed.status().setToBeUninstalled( ResStatus::APPL_HIGH );
527 ++opt_stats_r.chk_dropped;
528 _update_items.push_back( installed );
533 ///////////////////////////////////////////////////////////////////
534 // Now check the remembered packages and check non unique provided.
535 // Maybe one of them was somehow selected. Otherwise we have to guess
537 ///////////////////////////////////////////////////////////////////
538 MIL << "doUpgrade pass 2..." << endl;
540 // look at the ones with a single provide first
542 for ( TodoMap::iterator it = addProvided.begin(); it != addProvided.end(); ++it ) {
544 PoolItemOrderSet & tset( it->second ); // these are the providers (well, just one)
546 for ( PoolItemOrderSet::iterator sit = tset.begin(); sit != tset.end(); ++sit ) {
547 PoolItem provider (*sit);
549 if (setForInstallation (_pool, provider)) {
550 ++opt_stats_r.chk_replaced;
555 if ( doesObsoleteItem (provider, it->first ) ) {
556 it->first.status().setToBeUninstalled( ResStatus::APPL_HIGH );
562 // look at the ones with multiple providers
564 for ( TodoMap::iterator it = addMultiProvided.begin(); it != addMultiProvided.end(); ++it ) {
565 MIL << "GET ONE OUT OF " << it->second.size() << " for " << it->first << endl;
568 PoolItemOrderSet & gset( it->second );
570 for ( PoolItemOrderSet::iterator git = gset.begin(); git != gset.end(); ++git ) {
571 PoolItem item (*git);
573 if (git == gset.begin()) // default to first of set; the set is ordered, first is the best
576 if ( item.status().isToBeInstalled()) {
577 MIL << " ==> (pass 2: meanwhile set to install): " << item << endl;
578 if ( ! doesObsoleteItem (item, it->first ) ) {
579 it->first.status().setToBeUninstalled( ResStatus::APPL_HIGH );
584 // Be prepared to guess.
585 // Most common situation for guessing is something like:
587 // qt-devel-experimental
589 // That's why currently the shortest package name wins.
590 if ( !guess || guess->name().size() > item->name().size() ) {
597 // Checking if the selected provider depends on language, if yes try to find out the
598 // correct language package
599 bool requested_locale_match = false;
600 Capabilities freshens( guess->dep( Dep::FRESHENS ) );
602 // is this a language package ?
603 for (Capabilities::const_iterator cit = freshens.begin(); cit != freshens.end(); ++cit) {
604 string citName = cit->asString();
605 if (citName.length() > 7 && citName.compare(0, 7, "locale(") == 0) { // is a language dependency
606 requested_locale_match = true;
611 if (requested_locale_match) {
612 // searching the best language
613 PoolItemOrderSet & gset( it->second );
614 requested_locale_match = false;
616 for ( PoolItemOrderSet::iterator git = gset.begin(); git != gset.end(); ++git ) {
617 PoolItem item (*git);
619 if ( item.status().isToBeInstalled()) {
620 MIL << " ==> (pass 2: meanwhile set to install): " << item << endl;
621 if ( ! doesObsoleteItem (item, it->first ) ) {
622 it->first.status().setToBeUninstalled( ResStatus::APPL_HIGH );
627 freshens = item->dep( Dep::FRESHENS );
628 ZYpp::Ptr z = zypp::getZYpp();
629 ZYpp::LocaleSet requested_locales = z->getRequestedLocales();
631 // try to find a match of the locale freshens with one of the requested locales
633 for (Capabilities::const_iterator cit = freshens.begin(); cit != freshens.end(); ++cit) {
634 string citName = cit->asString();
635 if (citName.length() > 7 && citName.compare(0, 7, "locale(") == 0) { // is a language dependency
636 string loc = cit->index();
637 MIL << "Look for language fallback " << loc << ":" << item << endl;
638 if (requested_locales.find( Locale( loc ) ) != requested_locales.end()) {
639 MIL << "Locale '" << loc << "' is requested" << endl;
640 requested_locale_match = true;
647 if (requested_locale_match) break;
653 setForInstallation (_pool, guess);
654 MIL << " ==> REPLACED by: (pass 2: guessed): " << guess << endl;
655 if ( ! doesObsoleteItem (guess, it->first ) ) {
656 it->first.status().setToBeUninstalled( ResStatus::APPL_HIGH );
658 ++opt_stats_r.chk_replaced_guessed;
662 ///////////////////////////////////////////////////////////////////
664 ///////////////////////////////////////////////////////////////////
665 MIL << opt_stats_r << endl;
667 // Setting Resolver to upgrade mode
671 ///////////////////////////////////////////////////////////////////
672 };// namespace detail
673 /////////////////////////////////////////////////////////////////////
674 /////////////////////////////////////////////////////////////////////
675 };// namespace solver
676 ///////////////////////////////////////////////////////////////////////
677 ///////////////////////////////////////////////////////////////////////
679 /////////////////////////////////////////////////////////////////////////