QueueItemInstall.cc

Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
00002 /* QueueItemInstall.cc
00003  *
00004  * Copyright (C) 2000-2002 Ximian, Inc.
00005  * Copyright (C) 2005 SUSE Linux Products GmbH
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License,
00009  * version 2, as published by the Free Software Foundation.
00010  *
00011  * This program is distributed in the hope that it will be useful, but
00012  * WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  * General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00019  * 02111-1307, USA.
00020  */
00021 
00022 #include "zypp/CapFactory.h"
00023 #include "zypp/CapSet.h"
00024 #include "zypp/Package.h"
00025 #include "zypp/base/Logger.h"
00026 #include "zypp/base/String.h"
00027 #include "zypp/base/Gettext.h"
00028 
00029 #include "zypp/base/Algorithm.h"
00030 #include "zypp/ResPool.h"
00031 #include "zypp/ResFilters.h"
00032 #include "zypp/CapFilters.h"
00033 #include "zypp/CapMatchHelper.h"
00034 
00035 #include "zypp/solver/detail/QueueItemInstall.h"
00036 #include "zypp/solver/detail/QueueItemEstablish.h"
00037 #include "zypp/solver/detail/QueueItemUninstall.h"
00038 #include "zypp/solver/detail/QueueItemRequire.h"
00039 #include "zypp/solver/detail/QueueItemConflict.h"
00040 #include "zypp/solver/detail/QueueItem.h"
00041 #include "zypp/solver/detail/ResolverContext.h"
00042 #include "zypp/solver/detail/ResolverInfoConflictsWith.h"
00043 #include "zypp/solver/detail/ResolverInfoMisc.h"
00044 #include "zypp/solver/detail/ResolverInfoNeededBy.h"
00045 #include "zypp/solver/detail/Helper.h"
00046 
00048 namespace zypp
00049 { 
00050 
00051   namespace solver
00052   { 
00053 
00054     namespace detail
00055     { 
00056 
00057 using namespace std;
00058 
00059 IMPL_PTR_TYPE(QueueItemInstall);
00060 
00061 //---------------------------------------------------------------------------
00062 
00063 std::ostream &
00064 QueueItemInstall::dumpOn( std::ostream & os ) const
00065 {
00066     os << "[" << (_soft?"Soft":"") << "Install: ";
00067     os << _item;
00068     if (_upgrades) {
00069         os << ", Upgrades ";
00070         os << _upgrades;
00071     }
00072     if (_dep_satisfied_by_this_install != Capability::noCap) {
00073         os << ", Satisfies [" << _dep_satisfied_by_this_install << "]";
00074     }
00075     if (_needed_by) {
00076         os << ", Needed by " << _needed_by;
00077     }
00078     if (_explicitly_requested) os << ", Explicit !";
00079     os << "]";
00080     return os;
00081 }
00082 
00083 //---------------------------------------------------------------------------
00084 
00085 QueueItemInstall::QueueItemInstall (const ResPool & pool, PoolItem_Ref item, bool soft)
00086     : QueueItem (QUEUE_ITEM_TYPE_INSTALL, pool)
00087     , _item (item)
00088     , _soft (soft)
00089     , _dep_satisfied_by_this_install (Capability::noCap)
00090     , _needed_by (PoolItem_Ref())
00091     , _channel_priority (0)
00092     , _other_penalty (0)
00093     , _explicitly_requested (false)
00094 {
00095     Resolvable::constPtr res = item.resolvable();
00096 
00097     // Atoms are by default parallel installable (cf #181103)
00098     bool install_in_parallel = isKind<Atom>( res );
00099 
00100     // if its not an atom, check if its a package with 'install-only' set
00101     if (!install_in_parallel) {
00102         Package::constPtr pkg = asKind<Package>( res );
00103         install_in_parallel = (pkg != NULL) && pkg->installOnly();
00104     }
00105 
00106     // if its not parallel installable
00107     //   check if this install upgrades anything
00108 
00109     if (!install_in_parallel) {
00110         _upgrades = Helper::findInstalledItem (pool, item);
00111     }
00112 
00113     _XDEBUG("QueueItemInstall::QueueItemInstall(" << item << (soft?", soft":"") << ") upgrades " << _upgrades);
00114 }
00115  
00116 
00117 QueueItemInstall::~QueueItemInstall()
00118 {
00119 }
00120 
00121 //---------------------------------------------------------------------------
00122 
00123 bool
00124 QueueItemInstall::isSatisfied (ResolverContext_Ptr context) const
00125 {
00126     return context->isPresent (_item);
00127 }
00128 
00129 
00130 //---------------------------------------------------------------------------
00131 
00132 // Handle items which freshen or supplement us -> re-establish them
00133 
00134 // see also FreshenState in Resolver.cc
00135 // see also UninstallEstablishItem in QueueItemUninstall.cc
00136 
00137 typedef map<string, PoolItem_Ref> EstablishMap;
00138 
00139 struct InstallEstablishItem
00140 {
00141     EstablishMap establishmap;
00142 
00143     InstallEstablishItem ()
00144     { }
00145 
00146 
00147     // provider has a freshens on a just to-be-installed item
00148     //   re-establish provider, maybe its incomplete now
00149 
00150     bool operator()( const CapAndItem & cai )
00151     {
00152         _XDEBUG("QueueItemInstall::InstallEstablishItem (" << cai.item << ", " << cai.cap << ")");
00153 
00154         // only consider best architecture, best edition
00155 
00156         PoolItem_Ref item( cai.item );
00157 
00158         EstablishMap::iterator it = establishmap.find( item->name() );
00159 
00160         if (it != establishmap.end()) {                                 // item with same name found
00161             int cmp = it->second->arch().compare( item->arch() );
00162             if (cmp < 0) {                                              // new item has better arch
00163                 it->second = item;
00164             }
00165             else if (cmp == 0) {                                        // new item has equal arch
00166                 if (it->second->edition().compare( item->edition() ) < 0) {
00167                     it->second = item;                          // new item has better edition
00168                 }
00169             }
00170         }
00171         else {
00172             establishmap[item->name()] = item;
00173         }
00174         return true;
00175     }
00176 };
00177 
00178 
00179 
00180 //---------------------------------------------------------------------------
00181 
00182 // Handle items which conflict us -> uninstall them
00183 
00184 struct UninstallConflicting
00185 {
00186     ResolverContext_Ptr _context;
00187     const Capability _provided_cap;
00188     PoolItem _install_item;             // the to-be-installed item issuing the conflict
00189     PoolItem _upgrade_item;             // the installed, to-be-upgraded item (might be empty if its a fresh install)
00190     QueueItemList & _qil;
00191     bool ignored;
00192 
00193     UninstallConflicting( ResolverContext_Ptr ctx, const Capability & provided_cap, PoolItem install_item, PoolItem upgrade_item, QueueItemList & qil )
00194         : _context( ctx )
00195         , _provided_cap( provided_cap )
00196         , _install_item( install_item )
00197         , _upgrade_item( upgrade_item )
00198         , _qil( qil )
00199         , ignored( false )
00200     { }
00201 
00202 
00203     // conflicting_item provides a capability (conflicting_cap), _install_item lists as conflicts.
00204 
00205     bool operator()( const CapAndItem & cai)
00206     {
00207         PoolItem_Ref conflicting_item = cai.item;
00208 
00209         _XDEBUG("UninstallConflicting(" << conflicting_item << ", cap " << cai.cap << ")");
00210 
00211         if (conflicting_item == _install_item) {                                // self conflict ?
00212             WAR << "Ignoring self-conflicts" << endl;
00213             return true;
00214         }
00215         if (conflicting_item == _upgrade_item) {                                // upgrade conflict ?
00216             _XDEBUG("We're upgrading the conflicting item");
00217             return true;
00218         }
00219 
00220         const Capability conflicting_cap = cai.cap;
00221         ResolverInfo_Ptr log_info;
00222         QueueItemUninstall_Ptr uninstall_item;
00223 
00224         IgnoreMap ignoreMap = _context->getIgnoreConflicts();           // ignored conflict ?
00225         // checking for ignoring dependencies
00226         for (IgnoreMap::iterator it = ignoreMap.begin(); it != ignoreMap.end(); it++) {
00227             if (it->first == conflicting_item
00228                 && it->second == conflicting_cap)
00229             {
00230                 _XDEBUG("Found ignoring conflicts " << conflicting_cap << " for " << conflicting_item);
00231                 ignored = true;
00232                 return false;           // stop iteration
00233             } else {
00234                 _XDEBUG("Ignoring conflict " << it->second << " for " <<  it->first << " does not fit");            
00235             }
00236         }
00237 
00238         /* Check to see if we conflict with ourself and don't create
00239          * an uninstall item for it if we do.  This is Debian's way of
00240          * saying that one and only one item with this provide may
00241          * exist on the system at a time.
00242          */
00243 
00244         if (compareByNVR (conflicting_item.resolvable(), _install_item.resolvable()) == 0) {
00245                 return true;
00246         }
00247 
00248 #warning Make behaviour configurable
00249         // If the package is installed or is set to be installed by the user,
00250         // let the user decide deleting conflicting package. This is only an info.
00251         // Try at first updating packages.
00252         //
00253         ResStatus confl_status = _context->getStatus( conflicting_item );
00254         if (confl_status.isToBeInstalled()                      // scheduled for installation
00255             || confl_status.staysInstalled())                   // not scheduled at all but installed
00256         {
00257             ResolverInfoMisc_Ptr misc_info = new ResolverInfoMisc (RESOLVER_INFO_TYPE_CONFLICT_CANT_INSTALL,
00258                                                                                _install_item, RESOLVER_INFO_PRIORITY_VERBOSE, _provided_cap);
00259             misc_info->setOtherPoolItem (conflicting_item);
00260             misc_info->setOtherCapability (conflicting_cap);
00261             _context->addInfo (misc_info);
00262         }
00263 
00264         _XDEBUG("because: '" << conflicting_item << "'conflicts " << conflicting_cap);
00265 
00266         QueueItemUninstall_Ptr uninstall_qitem = new QueueItemUninstall (_context->pool(), conflicting_item, QueueItemUninstall::CONFLICT);
00267         uninstall_qitem->setDueToConflict ();
00268         uninstall_qitem->setPriority(0); // evaluate explizit uninstall first (greater priority) in order
00269                                           // to be sure, that this item still exist after the solver run would be finished. Bug 273918
00270         log_info = new ResolverInfoConflictsWith (conflicting_item, _install_item,
00271                                                   conflicting_cap);
00272         uninstall_qitem->addInfo (log_info);
00273         _qil.push_front (uninstall_qitem);
00274 
00275         return true;
00276     }
00277 };
00278 
00279 //---------------------------------------------------------------------------------------
00280 
00281 bool
00282 QueueItemInstall::process (const QueueItemList & mainQueue, ResolverContext_Ptr context, QueueItemList & qil)
00283 {
00284     ResStatus status = context->getStatus(_item);
00285 
00286     _XDEBUG( "QueueItemInstall::process(" << *this << "):" << status);
00287 
00288     /* Log which item need this install */
00289 
00290     if (_needed_by) {
00291         
00292         ResStatus neededStatus = context->getStatus(_needed_by);
00293         if (neededStatus.isToBeUninstalled()) {
00294             _XDEBUG( _needed_by << " will be uninstalled. So the requirement is not needed anymore");
00295             return true;
00296         }
00297 
00298         ResolverInfoNeededBy_Ptr info;
00299 
00300         info = new ResolverInfoNeededBy (_item);
00301         info->addRelatedPoolItem (_needed_by);
00302         info->setCapability (_dep_satisfied_by_this_install, _soft?Dep::RECOMMENDS:Dep::REQUIRES);
00303         info->setInitialInstallation (true);
00304         context->addInfo (info);
00305     }
00306 
00307     /* If we are trying to upgrade item A with item B and they both have the
00308         same version number, do nothing.  This shouldn't happen in general with
00309         zypp, but can come up with the installer & autopull. */
00310 
00311     if (_upgrades
00312         && compareByNVRA(_item.resolvable(), _upgrades.resolvable()) == 0)
00313     {
00314         if (_item->kind() == ResTraits<Package>::kind) {
00315             ResolverInfo_Ptr info;
00316             _DEBUG("install of " << _item << " upgrades itself, skipping");
00317 
00318             info = new ResolverInfoMisc (RESOLVER_INFO_TYPE_SKIPPING, _item, RESOLVER_INFO_PRIORITY_VERBOSE);
00319             context->addInfo (info);
00320             goto finished;
00321         }
00322         else {
00323             _DEBUG("re-install " << _item);
00324         }
00325     }
00326 
00327     // check if this install is still needed
00328     //   (maybe other resolver processing made this install obsolete
00329 
00330     if (_needed_by) {
00331         _XDEBUG( "still needed ");
00332 
00333         ResStatus status = _needed_by.status();
00334         _XDEBUG("by: [status: " << status << "] " << _needed_by);
00335         if ( status.isToBeUninstalled()
00336             || status.isImpossible())
00337         {
00338             goto finished;          
00339         }
00340     }
00341 
00342     /* If we are in verify mode and this install is about to fail, don't let it happen...
00343            instead, we try to back out of the install by removing whatever it was that
00344            needed this. */
00345 
00346     if (context->verifying()
00347         && (status.isToBeUninstalled() || status.isImpossible())
00348         && _needed_by) {
00349 
00350         QueueItemUninstall_Ptr uninstall_item;
00351         uninstall_item = new QueueItemUninstall (pool(), _needed_by, QueueItemUninstall::BACKOUT);
00352         qil.push_front (uninstall_item);
00353 
00354         goto finished;
00355     }
00356 
00357     // If this install is due to a needed, convert it to a normal install
00358 
00359     if (status.isNeeded()) {
00360         context->setStatus (_item, _soft ? ResStatus::toBeInstalledSoft :  ResStatus::toBeInstalled);
00361     }
00362 
00363 
00364     // if this install upgrades an installed resolvable, explicitly uninstall this one
00365     //   in order to ensure that all dependencies are still met after the upgrade
00366 
00367     if (!_upgrades) {
00368 
00369         _XDEBUG("trying simple install of " <<  _item);
00370         if (!context->install (_item, context->verifying() || _soft, _other_penalty))
00371             goto finished;
00372 
00373     }
00374     else {
00375 
00376         QueueItemUninstall_Ptr uninstall_item;
00377 
00378         _XDEBUG("trying upgrade install of " << _item);
00379 
00380         if (!context->upgrade (_item, _upgrades, context->verifying() || _soft, _other_penalty)) {
00381             // invalid solution
00382             ResolverInfo_Ptr info = new ResolverInfoMisc (RESOLVER_INFO_TYPE_INVALID_SOLUTION, PoolItem_Ref(), RESOLVER_INFO_PRIORITY_VERBOSE);
00383             context->addError (info);
00384             goto finished;
00385         }
00386 
00387         // the upgrade will uninstall the installed one, take care of this
00388 
00389         uninstall_item = new QueueItemUninstall (pool(), _upgrades, QueueItemUninstall::UPGRADE );
00390         uninstall_item->setUpgradedTo (_item);
00391 
00392         if (_explicitly_requested)
00393             uninstall_item->setExplicitlyRequested ();
00394 
00395         qil.push_front (uninstall_item);
00396 
00397     }
00398 
00399 
00400     // we're done if this isn't currently uninstalled or incomplete
00401 
00402     if (! (status.staysUninstalled()
00403            || status.isToBeUninstalledDueToUnlink()
00404            || status.isIncomplete()
00405            || status.isSatisfied()))
00406     {
00407         _XDEBUG("status " << status << " -> finished");
00408         goto finished;
00409     }
00410 
00411     _XDEBUG("status " << status << " -> NOT finished");
00412     {   // just a block for local initializers, the goto above makes this necessary
00413 
00414         ResolverInfoMisc_Ptr misc_info;
00415 
00416         if (_upgrades) {
00417 
00418             misc_info = new ResolverInfoMisc (RESOLVER_INFO_TYPE_UPDATING, _item, RESOLVER_INFO_PRIORITY_VERBOSE);
00419             misc_info->setOtherPoolItem (_upgrades);
00420 
00421         } else {
00422 
00423             misc_info = new ResolverInfoMisc (RESOLVER_INFO_TYPE_INSTALLING, _item, RESOLVER_INFO_PRIORITY_VERBOSE);
00424 
00425         }
00426 
00427         context->addInfo (misc_info);
00428         logInfo (context);
00429 
00430         /* Construct require items for each of the item's requires that is still unsatisfied. */
00431 
00432         CapSet caps;
00433 
00434         caps = _item->dep (Dep::REQUIRES);
00435 
00436         for (CapSet::const_iterator iter = caps.begin(); iter != caps.end(); iter++) {
00437 
00438             const Capability cap = *iter;
00439             _XDEBUG("this requires " << cap);
00440             bool fulfilled = false;
00441             
00442             fulfilled = context->requirementIsInstalledOrUnneeded (cap, _item, Dep::REQUIRES);
00443 
00444             if (!fulfilled) {
00445                 _XDEBUG("this requirement is still unfulfilled");
00446                 QueueItemRequire_Ptr req_item = new QueueItemRequire (pool(), cap );
00447                 req_item->addPoolItem (_item);
00448                 qil.push_front (req_item);
00449             }           
00450         }
00451 
00452         caps = _item->dep (Dep::RECOMMENDS);
00453 
00454         for (CapSet::const_iterator iter = caps.begin(); iter != caps.end(); iter++) {
00455 
00456             const Capability cap = *iter;
00457             _XDEBUG("this recommends " << cap);
00458 
00459             if (!context->requirementIsMet (cap, _item, Dep::RECOMMENDS)) {
00460                 _XDEBUG("this recommends is still unfulfilled");
00461                 QueueItemRequire_Ptr req_item = new QueueItemRequire (pool(), cap, true);       // this is a soft requires
00462                 req_item->addPoolItem (_item);
00463                 qil.push_front (req_item);
00464             }
00465 
00466         }
00467 
00468         /* Construct conflict items for each of the item's conflicts. */
00469 
00470         caps = _item->dep (Dep::CONFLICTS);
00471         for (CapSet::const_iterator iter = caps.begin(); iter != caps.end(); iter++) {
00472             const Capability cap = *iter;
00473             _XDEBUG("this conflicts with '" << cap << "'");
00474             QueueItemConflict_Ptr conflict_item = new QueueItemConflict (pool(), cap, _item );
00475             // Push the QueueItem at the END of the list in order to favourite conflicts caused
00476             // by obsolating this item.
00477             qil.push_back (conflict_item);
00478         }
00479 
00480         /* Construct conflict items for each of the item's obsoletes. */
00481 
00482         caps = _item->dep (Dep::OBSOLETES);
00483         IgnoreMap ignoreMap = context->getIgnoreObsoletes();
00484         
00485         for (CapSet::const_iterator iter = caps.begin(); iter != caps.end(); iter++) {
00486             const Capability cap = *iter;
00487             bool found = false;
00488             for (IgnoreMap::iterator it = ignoreMap.begin();
00489                  it != ignoreMap.end(); it++) {
00490                 if (it->first == _item
00491                     && it->second == cap) {
00492                     _XDEBUG("Found ignoring obsoletes " << cap << " for " << _item);
00493                     found = true;
00494                     break;
00495                 }
00496             }
00497             if (!found) {           
00498                 _XDEBUG("this obsoletes " <<  cap);
00499                 QueueItemConflict_Ptr conflict_item = new QueueItemConflict (pool(), cap, _item );
00500                 conflict_item->setActuallyAnObsolete();
00501                 // Push the QueueItem at the BEGIN of the list in order to favourite this confict
00502                 // comparing to "normal" conflicts, cause this item will be deleted. So other
00503                 // conflicts will not be regarded in the future.
00504                 qil.push_front (conflict_item);
00505             }
00506         }
00507 
00508         // Go over each provides of the to-be-uninstalled item and
00509         // - re-establish any freshens
00510         // - re-establish any supplements
00511         // - find items that conflict with us and try to uninstall it if it is useful
00512 
00513         InstallEstablishItem establish;
00514 
00515         caps = _item->dep (Dep::PROVIDES);
00516         bool ignored = false;
00517 
00518         for (CapSet::const_iterator iter = caps.begin(); iter != caps.end(); iter++) {
00519             const Capability cap = *iter;
00520 
00521             /* Construct establish items for each of those which
00522                 freshen or supplement and provides of this resolvable. */
00523 
00524             _XDEBUG("Re-establish all freshens on " << cap);
00525             // pool ()->foreachFresheningResItem (cap, establish_freshens_cb, &info);
00526 
00527             Dep dep( Dep::FRESHENS);
00528             invokeOnEach( pool().byCapabilityIndexBegin( cap.index(), dep ), // begin()
00529                           pool().byCapabilityIndexEnd( cap.index(), dep ),   // end()
00530                           resfilter::ByCapMatch( cap ),
00531                           functor::functorRef<bool,CapAndItem>( establish ) );
00532 
00533             dep = Dep::SUPPLEMENTS;
00534             invokeOnEach( pool().byCapabilityIndexBegin( cap.index(), dep ), // begin()
00535                           pool().byCapabilityIndexEnd( cap.index(), dep ),   // end()
00536                           resfilter::ByCapMatch( cap ),
00537                           functor::functorRef<bool,CapAndItem>( establish ) );
00538 
00539             if (!ignored) {
00540                 // Search items that conflict with us and try to uninstall it if it is useful
00541 
00542                 UninstallConflicting info( context, cap, _item, _upgrades, qil );
00543 
00544                 Dep dep( Dep::CONFLICTS );
00545                 invokeOnEach( pool().byCapabilityIndexBegin( cap.index(), dep ),
00546                               pool().byCapabilityIndexEnd( cap.index(), dep ),
00547                               resfilter::ByCapMatch( cap ),
00548                               functor::functorRef<bool,CapAndItem>( info ) );
00549 
00550                 ignored = info.ignored;         // user choose to ignore these conflitcs
00551             }
00552 
00553         } // iterate over all provides
00554 
00555         // schedule all collected items for establish
00556 
00557         for (EstablishMap::iterator firstIt = establish.establishmap.begin(); firstIt != establish.establishmap.end(); ++firstIt) {
00558             bool conflictFound = false;
00559             CapSet provides = firstIt->second.resolvable()->deps()[Dep::PROVIDES];
00560             // It is useless to establish items which are conflicting with eachother. So they will
00561             // be filtered out. bug 243595
00562             for (EstablishMap::iterator secondIt = firstIt; secondIt != establish.establishmap.end() && !conflictFound; ++secondIt) {
00563                 CapSet conflicts = secondIt->second.resolvable()->deps()[Dep::CONFLICTS];
00564                 if (hasMatches (provides,conflicts)) {
00565                     conflictFound = true;
00566                     _XDEBUG("Do not establish " << firstIt->second << " cause it is conflicting with " << secondIt->second );
00567                 }
00568             }
00569 
00570             if (!conflictFound) {
00571                 QueueItemEstablish_Ptr establish_item = new QueueItemEstablish (pool(), firstIt->second, true);
00572                 qil.push_front( establish_item );
00573             }
00574         }
00575 
00576     } // end of goto-over-definitions-to-finished block
00577 
00578  finished:
00579 
00580     return true;
00581 }
00582 
00583 
00584 QueueItem_Ptr
00585 QueueItemInstall::copy (void) const
00586 {
00587     QueueItemInstall_Ptr new_install = new QueueItemInstall (pool(), _item);
00588     new_install->QueueItem::copy(this);
00589 
00590     new_install->_upgrades = _upgrades;
00591     new_install->_dep_satisfied_by_this_install = _dep_satisfied_by_this_install;
00592     new_install->_needed_by = _needed_by;
00593     new_install->_channel_priority = _channel_priority;
00594     new_install->_other_penalty = _other_penalty;
00595     new_install->_explicitly_requested = _explicitly_requested;
00596 
00597     return new_install;
00598 }
00599 
00600 
00601 int
00602 QueueItemInstall::cmp (QueueItem_constPtr item) const
00603 {
00604     int cmp = this->compare (item);
00605     if (cmp != 0)
00606         return cmp;
00607     QueueItemInstall_constPtr install = dynamic_pointer_cast<const QueueItemInstall>(item);
00608     return compareByNVRA (_item.resolvable(), install->_item.resolvable());
00609 }
00610 
00611 
00613     };// namespace detail
00616   };// namespace solver
00619 };// namespace zypp
00621 

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