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/Capability.h"
50 #include "zypp/VendorAttr.h"
51 #include "zypp/Package.h"
52 #include "zypp/ZYppFactory.h"
53 #include "zypp/solver/detail/Types.h"
54 #include "zypp/solver/detail/Helper.h"
55 #include "zypp/solver/detail/Resolver.h"
56 #include "zypp/solver/detail/Testcase.h"
57 #include "zypp/solver/detail/SATResolver.h"
58 #include "zypp/ResolverProblem.h"
59 #include "zypp/ProblemSolution.h"
60 #include "zypp/Target.h"
62 /////////////////////////////////////////////////////////////////////////
64 { ///////////////////////////////////////////////////////////////////////
65 ///////////////////////////////////////////////////////////////////////
67 { /////////////////////////////////////////////////////////////////////
68 /////////////////////////////////////////////////////////////////////
70 { ///////////////////////////////////////////////////////////////////
75 /** Order on AvialableItemSet.
78 * \li ResObject::constPtr as fallback.
80 struct AVOrder : public std::binary_function<PoolItem,PoolItem,bool>
82 // NOTE: operator() provides LESS semantics to order the set.
83 // So LESS means 'prior in set'. We want 'better' archs and
84 // 'better' editions at the beginning of the set. So we return
85 // TRUE if (lhs > rhs)!
87 bool operator()( const PoolItem lhs, const PoolItem rhs ) const
89 int res = lhs->arch().compare( rhs->arch() );
92 res = lhs->edition().compare( rhs->edition() );
96 // no more criteria, still equal:
97 // use the ResObject::constPtr (the poiner value)
98 // (here it's arbitrary whether < or > )
99 return lhs.resolvable() < rhs.resolvable();
103 typedef std::set<PoolItem, AVOrder> PoolItemOrderSet;
107 // check if downgrade is allowed
108 // (Invariant on entry: installed.edition >= candidate.edition)
110 // candidate must have allowed vendor (e.g. 'SuSE', 'Novell', ...) and candidates buildtime must be
114 downgrade_allowed( PoolItem installed, PoolItem candidate, bool silent_downgrades )
116 if (installed.status().isLocked()) {
117 MIL << "Installed " << installed << " is locked, not upgrading" << endl;
121 Resolvable::constPtr ires = installed.resolvable();
122 Package::constPtr ipkg = asKind<Package>(ires);
123 Resolvable::constPtr cres = candidate.resolvable();
124 Package::constPtr cpkg = asKind<Package>(cres);
127 DBG << "Installed vendor '" << ipkg->vendor() << "'" << endl;
129 DBG << "Candidate vendor '" << cpkg->vendor() << "'" << endl;
132 && VendorAttr::instance().equivalent( ipkg->vendor(), cpkg->vendor() ) )
134 if ( silent_downgrades )
136 if ( ipkg->buildtime() <= cpkg->buildtime() ) { // installed has older or equal buildtime
137 MIL << "allowed downgrade " << installed << " to " << candidate << endl;
138 return true; // see bug #152760
146 Resolver::doesObsoleteItem (PoolItem candidate, PoolItem installed)
148 return _satResolver->doesObsoleteItem (candidate, installed);
151 //-----------------------------------------------------------------------------
154 //-----------------------------------------------------------------------------
156 // Selecting item for installation
158 class LookForSelected : public resfilter::PoolItemFilterFunctor
164 LookForSelected (PoolItem can)
169 bool operator()( PoolItem item )
171 if (item.status().isToBeInstalled()
172 && item->edition() == candidate->edition()
173 && item->arch() == candidate->arch()) {
174 MIL << item << " is already selected for installation --> ignoring" << endl;
176 return false; // stop here
182 bool setForInstallation (const ResPool &pool, PoolItem item) {
183 LookForSelected info(item);
185 invokeOnEach( pool.byIdentBegin (item->kind(),item->name()),
186 pool.byIdentEnd (item->kind(),item->name()),
187 resfilter::ByUninstalled (), // ByUninstalled
188 functor::functorRef<bool,PoolItem> (info) );
190 MIL << " ---> " << item << " will be ignoring" << endl;
193 return item.status().setToBeInstalled( ResStatus::APPL_HIGH );
197 //-----------------------------------------------------------------------------
199 ///////////////////////////////////////////////////////////////////
202 // METHOD NAME : Resolver::doUpgrade
203 // METHOD TYPE : bool
205 // DESCRIPTION : go through all installed (but not yet touched by user)
206 // packages and look for update candidates
209 Resolver::doUpgrade( UpgradeStatistics & opt_stats_r )
211 typedef map<PoolItem,PoolItem> CandidateMap;
212 typedef map<PoolItem,PoolItemOrderSet> TodoMap;
214 CandidateMap candidatemap;
217 TodoMap addMultiProvided;
221 target = getZYpp()->target();
223 catch( const Exception & excpt_r) {
224 ERR << "Huh, no target ?";
225 ZYPP_CAUGHT(excpt_r);
226 if (!_testing) return false; // can't continue without target
227 MIL << "Running in test mode, continuing without target" << endl;
229 MIL << "target at " << target << endl;
231 MIL << "doUpgrade start... "
232 << "(silent_downgrades:" << (opt_stats_r.silent_downgrades?"yes":"no") << ")"
235 // create a testcase for the updating system
236 PathInfo path ("/mnt/var/log"); // checking if update has been started from instsys
238 if ( !path.isExist() ) {
239 Testcase testcase("/var/log/updateTestcase");
240 testcase.createTestcase (*this, true, false); // create pool, do not solve
242 Testcase testcase("/mnt/var/log/updateTestcase");
243 testcase.createTestcase (*this, true, false); // create pool, do not solve
246 _unmaintained_items.clear();
247 _problem_items.clear();
249 UpgradeOptions opts( opt_stats_r );
250 opt_stats_r = UpgradeStatistics();
251 (UpgradeOptions&)opt_stats_r = opts;
254 /* Find upgrade candidates for each package. */
256 for ( ResPool::const_iterator it = _pool.begin(); it != _pool.end(); ++it ) {
261 if ( item.status().isToBeUninstalled() ) {
262 MIL << "doUpgrade available: SKIP to delete " << item << endl;
263 ++opt_stats_r.pre_todel;
266 if ( item.status().isLocked() ) {
267 MIL << "doUpgrade available: SKIP locked " << item << endl;
268 if ( item.status().staysInstalled() ) {
269 ++opt_stats_r.pre_nocand;
274 if ( item.status().staysInstalled() ) { // installed item
276 CandidateMap::const_iterator cand_it = candidatemap.find( installed );
277 if (cand_it != candidatemap.end()) {
278 candidate = cand_it->second; // found candidate already
281 candidate = Helper::findUpdateItem( _pool, installed ); // find 'best' upgrade candidate
284 MIL << "doUpgrade available: SKIP no candidate for " << installed << endl;
285 ++opt_stats_r.pre_nocand;
288 if (candidate.status().isSeen()) { // seen already
289 candidate.status().setSeen(true);
292 candidate.status().setSeen(true); // mark as seen
293 candidatemap[installed] = candidate;
295 else { // assume Uninstalled
296 if (item.status().isSeen()) { // seen already
297 item.status().setSeen(true);
301 candidate.status().setSeen(true); // mark as seen
302 installed = Helper::findInstalledItem( _pool, candidate );
303 if (installed) { // check if we already have an installed
304 if ( installed.status().isLocked() ) {
305 MIL << "doUpgrade available: SKIP candidate " << candidate << ", locked " << installed << endl;
309 if ( !VendorAttr::instance().equivalent(installed->vendor(), candidate->vendor()) )
311 MIL << "Discarding '" << candidate << "' from vendor '"
312 << candidate->vendor() << "' different to installed '"
313 << installed->vendor() << "' vendor." << endl;
317 CandidateMap::const_iterator cand_it = candidatemap.find( installed );
318 if (cand_it == candidatemap.end() // not in map yet
319 || (cand_it->second->arch().compare( candidate->arch() ) < 0) // or the new has better architecture
320 || ((cand_it->second->arch().compare( candidate->arch() ) == 0) // or the new has the same architecture
321 && (cand_it->second->edition().compare( candidate->edition() ) < 0)) // and a better edition (-> 157501)
324 candidatemap[installed] = candidate; // put it in !
329 ++opt_stats_r.pre_avcand;
330 } // iterate over the complete pool
332 // reset all seen (for next run)
333 for ( ResPool::const_iterator it = _pool.begin(); it != _pool.end(); ++it ) {
334 it->status().setSeen( false );
337 MIL << "doUpgrade: " << opt_stats_r.pre_todel << " packages tagged to delete" << endl;
338 MIL << "doUpgrade: " << opt_stats_r.pre_nocand << " packages without candidate (foreign, replaced or dropped)" << endl;
339 MIL << "doUpgrade: " << opt_stats_r.pre_avcand << " packages available for update" << endl;
341 ///////////////////////////////////////////////////////////////////
342 // Now iterate installed packages, not selected to delete, and
343 // figure out what might be an appropriate replacement. Current
344 // packages state is changed immediately. Additional packages are
345 // reported but set to install later.
346 ///////////////////////////////////////////////////////////////////
347 MIL << "doUpgrade pass 1..." << endl;
349 for ( ResPool::const_iterator it = _pool.begin(); it != _pool.end(); ++it ) {
351 PoolItem installed(*it);
352 ResStatus status (installed.status());
354 if ( ! status.staysInstalled() ) {
357 ++opt_stats_r.chk_installed_total;
359 if ( status.transacts() ) { // we know its installed, if it transacts also
360 MIL << "SKIP to delete: " << installed.resolvable() << endl; // it'll be deleted
361 ++opt_stats_r.chk_already_todel;
365 if ( installed.status().isLocked() ) { // skip locked
366 MIL << "SKIP taboo: " << installed << endl;
367 ++opt_stats_r.chk_is_taboo;
368 _problem_items.push_back( installed ); // remember in problem list
372 if ( isKind<Patch>(installed.resolvable())
373 || isKind<Atom>(installed.resolvable())
374 || isKind<Script>(installed.resolvable())
375 || isKind<Message>(installed.resolvable())
376 || isKind<Pattern>(installed.resolvable()))
378 MIL << "Delete old: " << installed << endl;
379 installed.status().setToBeUninstalled( ResStatus::APPL_HIGH );
383 CandidateMap::iterator cand_it = candidatemap.find( installed );
385 bool can_be_dropped = false;
387 MIL << "REPLACEMENT FOR " << installed << endl;
388 ///////////////////////////////////////////////////////////////////
389 // figure out replacement
390 ///////////////////////////////////////////////////////////////////
391 if ( cand_it != candidatemap.end() ) {
393 PoolItem candidate (cand_it->second);
395 if ( ! candidate.status().isToBeInstalled() ) {
396 int cmp = installed->edition().compare( candidate->edition() );
397 if ( cmp < 0 ) { // new edition
398 setForInstallation (_pool,candidate);
399 MIL << " ==> INSTALL (new version): " << candidate << endl;
400 ++opt_stats_r.chk_to_update;
401 } else { // older or equal edition
402 // check whether to downgrade:
404 if (cmp == 0 // equal
405 || !downgrade_allowed( installed, candidate,
406 opt_stats_r.silent_downgrades) ) // or downgrade not allowed
408 MIL << " ==> (keep installed)" << candidate << endl; // keep installed
409 ++opt_stats_r.chk_to_keep_installed;
410 } else {// older and downgrade allowed
411 setForInstallation (_pool, candidate);
412 MIL << " ==> INSTALL (SuSE version downgrade): " << candidate << endl;
413 ++opt_stats_r.chk_to_downgrade;
417 MIL << " ==> INSTALL (preselected): " << candidate << endl;
418 ++opt_stats_r.chk_already_toins;
422 else { // no candidate
424 // replaced or dropped (anyway there's no candidate for this!)
425 // If unique provides exists check if obsoleted (replaced).
426 // Remember new package for 2nd pass.
428 Capability installedCap( installed->name(), Rel::EQ, installed->edition(), installed->kind());
430 // find ALL providers
431 sat::WhatProvides possibleProviders(installedCap);
433 // find best available providers for installed name
434 typedef map<string, PoolItem> FindMap;
435 FindMap providersMap; // the best providers which matched
436 bool otherVendorFound = false;
437 for_( iter, possibleProviders.begin(), possibleProviders.end() ) {
438 PoolItem provider = ResPool::instance().find( *iter );
439 if ( !VendorAttr::instance().equivalent(provider->vendor(), installed->vendor()) )
441 MIL << "Discarding '" << provider << "' from vendor '"
442 << provider->vendor() << "' different to uninstalled '"
443 << installed->vendor() << "' vendor." << endl;
444 otherVendorFound = true;
445 } else if ( provider.status().isToBeUninstalled() ) {
446 MIL << " IGNORE relation match (package is tagged to delete): " << provider << endl;
447 } else if ( provider.status().isInstalled() ) {
448 if (installed->name() == provider->name()) {
449 MIL << " IGNORE relation match (package is installed): " << provider << endl;
451 MIL << " Take installed package ONLY: " << provider << endl;
452 providersMap.clear();
457 FindMap::iterator it = providersMap.find( provider->name() );
459 if (it != providersMap.end()) { // provider with same name found
460 if (provider.status().isToBeInstalled()
461 || it->second.status().isToBeInstalled()) {
463 if (provider.status().isToBeInstalled()
464 && it->second.status().isToBeInstalled()) {
465 ERR << "only one should be set for installation: " << it->second << "; " << provider << endl;
467 if (provider.status().isToBeInstalled()) {
468 it->second = provider; // take thatone which is already set for installation
472 // not the same --> find better provider
473 int cmp = it->second->arch().compare( provider->arch() );
474 if (cmp < 0) { // new provider has better arch
475 it->second = provider;
477 else if (cmp == 0) { // new provider has equal arch
478 if (it->second->edition().compare( provider->edition() ) < 0) {
479 it->second = provider; // new provider has better edition
485 providersMap[provider->name()] = provider;
490 _DEBUG("lookup " << providersMap.size() << " provides for installed " << installedCap);
492 // copy from map to set
493 PoolItemOrderSet providers;
494 for (FindMap::const_iterator mapit = providersMap.begin(); mapit != providersMap.end(); ++mapit) {
495 providers.insert( mapit->second );
498 switch ( providersMap.size() ) {
500 if (otherVendorFound) {
501 MIL << " only resolvable with other vendor found ==> do nothing" << endl;
503 MIL << " ==> dropp if it does not fit to the system" << endl;
504 can_be_dropped = true;
508 addProvided[installed] = providers;
509 MIL << " ==> REPLACED by: " << (*providers.begin()) << endl;
511 // check obsoletes later
514 addMultiProvided[installed] = providers;
515 MIL << " ==> pass 2 (" << providers.size() << " times provided)" << endl;
517 // check obsoletes later
524 ///////////////////////////////////////////////////////////////////
525 // now handle dropped package
526 ///////////////////////////////////////////////////////////////////
528 if ( can_be_dropped ) {
529 _unmaintained_items.insert( installed );
534 ///////////////////////////////////////////////////////////////////
535 // Now check the remembered packages and check non unique provided.
536 // Maybe one of them was somehow selected. Otherwise we have to guess
538 ///////////////////////////////////////////////////////////////////
539 MIL << "doUpgrade pass 2..." << endl;
541 // look at the ones with a single provide first
543 for ( TodoMap::iterator it = addProvided.begin(); it != addProvided.end(); ++it ) {
545 PoolItemOrderSet & tset( it->second ); // these are the providers (well, just one)
547 for ( PoolItemOrderSet::iterator sit = tset.begin(); sit != tset.end(); ++sit ) {
548 PoolItem provider (*sit);
550 if (setForInstallation (_pool, provider)) {
551 ++opt_stats_r.chk_replaced;
556 if ( doesObsoleteItem (provider, it->first ) ) {
557 it->first.status().setToBeUninstalled( ResStatus::APPL_HIGH );
563 // look at the ones with multiple providers
565 for ( TodoMap::iterator it = addMultiProvided.begin(); it != addMultiProvided.end(); ++it ) {
566 MIL << "GET ONE OUT OF " << it->second.size() << " for " << it->first << endl;
569 PoolItemOrderSet & gset( it->second );
571 for ( PoolItemOrderSet::iterator git = gset.begin(); git != gset.end(); ++git ) {
572 PoolItem item (*git);
574 if (git == gset.begin()) // default to first of set; the set is ordered, first is the best
577 if ( item.status().isToBeInstalled()) {
578 MIL << " ==> (pass 2: meanwhile set to install): " << item << endl;
579 if ( ! doesObsoleteItem (item, it->first ) ) {
580 it->first.status().setToBeUninstalled( ResStatus::APPL_HIGH );
585 // Be prepared to guess.
586 // Most common situation for guessing is something like:
588 // qt-devel-experimental
590 // That's why currently the shortest package name wins.
591 if ( !guess || guess->name().size() > item->name().size() ) {
598 // Checking if the selected provider depends on language, if yes try to find out the
599 // correct language package
600 if (guess.satSolvable().supportsLocales()) { // is this a language package ?
601 // searching a package which provides one of the requested languages.
602 PoolItemOrderSet & gset( it->second );
603 MIL << "Item " << guess << " provides language support. Look for proper language items." << endl;
604 for ( PoolItemOrderSet::iterator git = gset.begin(); git != gset.end(); ++git ) {
605 PoolItem item (*git);
607 if ( item.status().isToBeInstalled()) {
608 MIL << " ==> (pass 2: meanwhile set to install): " << item << ". Ignoring preselections." << endl;
609 if ( ! doesObsoleteItem (item, it->first ) ) {
610 it->first.status().setToBeUninstalled( ResStatus::APPL_HIGH );
615 if (item.satSolvable().supportsRequestedLocales()) {
616 MIL << item << " provides a requested locale --> take thatone" << endl;
618 // do not break here cause there could be an item in the list which has been
619 // already set for installation.
627 setForInstallation (_pool, guess);
628 MIL << " ==> REPLACED by: (pass 2: guessed): " << guess << endl;
629 if ( ! doesObsoleteItem (guess, it->first ) ) {
630 it->first.status().setToBeUninstalled( ResStatus::APPL_HIGH );
632 ++opt_stats_r.chk_replaced_guessed;
636 ///////////////////////////////////////////////////////////////////
638 ///////////////////////////////////////////////////////////////////
639 MIL << opt_stats_r << endl;
641 // Setting Resolver to upgrade mode
644 // Unmaintained packages which does not fit to the updated system
645 // (broken dependencies) will be deleted.
646 // Make a solverrun and return it to the calling function
647 return checkUnmaintainedItems ();
652 ///////////////////////////////////////////////////////////////////
653 };// namespace detail
654 /////////////////////////////////////////////////////////////////////
655 /////////////////////////////////////////////////////////////////////
656 };// namespace solver
657 ///////////////////////////////////////////////////////////////////////
658 ///////////////////////////////////////////////////////////////////////
660 /////////////////////////////////////////////////////////////////////////