InstallOrder.cc

Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* InstallOrder.cc
00003  *
00004  * Copyright (C) 2005 SUSE Linux Products GmbH
00005  *
00006  * This program is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU General Public License,
00008  * version 2, as published by the Free Software Foundation.
00009  *
00010  * This program is distributed in the hope that it will be useful, but
00011  * WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013  * General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program; if not, write to the Free Software
00017  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00018  * 02111-1307, USA.
00019  */
00020 
00021 // stolen from yast2-packagemanager
00022 /*
00023    File:       InstallOrder.h
00024    Purpose:    Determine order for installing packages
00025    Author:     Ludwig Nussel <lnussel@suse.de>
00026    Maintainer: Ludwig Nussel <lnussel@suse.de>
00027 
00028 /-*/
00029 
00030 #include "zypp/solver/detail/InstallOrder.h"
00031 #include "zypp/base/LogTools.h"
00032 #include "zypp/base/Iterator.h"
00033 #include "zypp/base/Algorithm.h"
00034 
00035 #include "zypp/ResFilters.h"
00036 #include "zypp/ResStatus.h"
00037 #include "zypp/CapAndItem.h"
00038 #include "zypp/NameKindProxy.h"
00039 
00041 namespace zypp
00042 { 
00043 
00044   namespace solver
00045   { 
00046 
00047     namespace detail
00048     { 
00049 
00050 using namespace std;
00051 using namespace zypp;
00052 
00053 #define ITEMNAME(item) (item)->name()
00054 //-----------------------------------------------------------------------------
00055 
00056 InstallOrder::InstallOrder( const ResPool & pool, const PoolItemSet & toinstall, const PoolItemSet & installed )
00057     : _pool( pool )
00058     , _toinstall( toinstall )
00059     , _installed( installed )
00060     , _dirty (true)
00061     , _numrun (0)
00062 {
00063     _DEBUG("InstallOrder::InstallOrder(_toinstall " << _toinstall.size() << " items, _installed " << _installed.size() << " items)");
00064 }
00065 
00066 
00067 //-----------------------------------------------------------------------------
00068 
00069 const void
00070 InstallOrder::printAdj (std::ostream& os, bool reversed) const
00071 {
00072     const Graph& g = (reversed ? _rgraph : _graph);
00073     os << "digraph pkgdeps {" << endl;
00074     for (Graph::const_iterator gcit = g.begin(); gcit != g.end(); ++gcit)
00075     {
00076         Nodes::const_iterator niit = _nodes.find(gcit->first);
00077         int order = niit->second.order;
00078         string name = gcit->first->name();
00079         os << "\"" << name << "\"" << "[label=\"" << name << "\\n" << order << "\"";
00080         os << "] " << endl;
00081         for (PoolItemList::const_iterator scit = gcit->second.begin(); scit != gcit->second.end(); ++scit)
00082         {
00083             os << "\"" << name << "\" -> \"" << (*scit)->name() << "\"" << endl;
00084         }
00085     }
00086     os << "}" << endl;
00087 }
00088 
00089 
00090 //-----------------------------------------------------------------------------
00091 
00092 // yea, that stuff is suboptimal. there should be a heap sorted by order
00093 PoolItemList
00094 InstallOrder::computeNextSet()
00095 {
00096     PoolItemList newlist;
00097 
00098     if (_dirty) startrdfs();
00099 
00100     for (Nodes::iterator it = _nodes.begin(); it != _nodes.end(); ++it)
00101     {
00102         if (it->second.order == 0
00103             && it->second.item)                 // the default Nodes constructor leaves this empty
00104         {
00105             if (isKind<SystemResObject>( it->second.item.resolvable() ))
00106                 continue;
00107 
00108             XXX << "InstallOrder::computeNextSet found " << ITEMNAME(it->second.item) << endl;
00109 
00110             newlist.push_back(it->second.item);
00111         }
00112     }
00113 
00114     return newlist;
00115 }
00116 
00117 
00118 // decrease order of every adjacent node
00119 void
00120 InstallOrder::setInstalled(PoolItem_Ref item )
00121 {
00122     _dirty = true;
00123 
00124     PoolItemList adj = _rgraph[item];
00125 
00126     XXX << "InstallOrder::setInstalled " << ITEMNAME(item) << endl;
00127 
00128     // order will be < 0
00129     _nodes[item].order--;
00130     _installed.insert( item );
00131     _toinstall.erase( item );
00132 
00133     for (PoolItemList::iterator it = adj.begin(); it != adj.end(); ++it)
00134     {
00135         NodeInfo& info = _nodes[*it];
00136         info.order--;
00137         if (info.order < 0)
00138         {
00139             WAR << "order of node " << (*it) << " is < 0" << endl;
00140         }
00141     }
00142 }
00143 
00144 
00145 void
00146 InstallOrder::setInstalled( const PoolItemList & rl )
00147 {
00148     for (PoolItemList::const_iterator it = rl.begin(); it != rl.end(); ++it)
00149     {
00150         setInstalled(*it);
00151     }
00152 }
00153 
00154 //-----------------------------------------------------------------------------
00155 
00156 bool
00157 InstallOrder::doesProvide( const Capability requirement, PoolItem_Ref item ) const
00158 {
00159     CapSet::const_iterator pend = item->dep( Dep::PROVIDES ).end();
00160     for( CapSet::const_iterator pit = item->dep( Dep::PROVIDES ).begin(); pit != pend; ++pit) {
00161         if( pit->matches( requirement ) == CapMatch::yes ) {
00162             return item;
00163         }
00164     }
00165     return PoolItem_Ref();
00166 }
00167 
00168 
00169 PoolItem_Ref
00170 InstallOrder::findProviderInSet( const Capability requirement, const PoolItemSet & candidates ) const
00171 {
00172     for( PoolItemSet::const_iterator citer = candidates.begin(); citer != candidates.end(); citer++) {
00173         if( doesProvide( requirement, *citer ) ) {
00174             return *citer;
00175         }
00176     }
00177 
00178     return PoolItem_Ref();
00179 }
00180 
00181 struct CollectProviders
00182 {
00183     const PoolItem_Ref requestor;
00184     PoolItemList result;
00185     const PoolItemSet & limitto;                // limit search to members of this set
00186 
00187     CollectProviders (const PoolItem_Ref pi, const PoolItemSet & limit)
00188         : requestor (pi)
00189         , limitto (limit)
00190     { }
00191 
00192 
00193     bool operator()( const CapAndItem & c_and_i )
00194     {
00195         // item provides cap which matches a requirement from info->requestor
00196         //   this function gets _all_ providers and filter out those which are
00197         //   either installed or in our toinstall input list
00198         //
00199 XXX << "info(" << c_and_i.item <<")"<< endl;
00200         if ((c_and_i.item.resolvable() != requestor.resolvable())       // resolvable could provide its own requirement
00201             && (limitto.find( c_and_i.item ) != limitto.end()))         // limit to members of 'limitto' set
00202         {
00203             XXX << "tovisit " << ITEMNAME(c_and_i.item) << endl;
00204             result.push_back (c_and_i.item);
00205         }
00206 
00207         return true;
00208     }
00209 
00210 };
00211 
00212 //-----------------------------------------------------------------------------
00213 
00214 
00215 void
00216 InstallOrder::rdfsvisit (const PoolItem_Ref item)
00217 {
00218     typedef list<Capability> CapList;
00219     CapList requires;
00220 
00221     XXX << "InstallOrder::rdfsvisit, visiting " << ITEMNAME(item) << endl;
00222 
00223     NodeInfo& nodeinfo = _nodes[item];
00224 
00225     nodeinfo.visited = true;
00226     nodeinfo.begintime = _rdfstime;
00227     _rdfstime++;
00228 
00229     // items prereq
00230     CapSet prq( item->dep(Dep::PREREQUIRES) );
00231     // an installed items prereq (in case they are reqired for uninstall scripts)
00232     NameKindProxy nkp( _pool, item->name(), item->kind() );
00233     if ( ! nkp.installedEmpty() )
00234     {
00235       prq.insert( (*nkp.installedBegin())->dep(Dep::PREREQUIRES).begin(),
00236                   (*nkp.installedBegin())->dep(Dep::PREREQUIRES).end() );
00237     }
00238     // put prerequires first and requires last on list to ensure
00239     // that prerequires are processed first
00240     for (CapSet::const_iterator it = prq.begin(); it != prq.end(); ++it)
00241     {
00242         requires.push_back(*it);
00243     }
00244 
00245     // Product requirements are ignored to assert Product gets installed
00246     // as early as possible. Some stuff depends on it (e.g. registration).
00247     if ( ! isKind<Product>( item.resolvable() ) )
00248       {
00249         for (CapSet::const_iterator it = item->dep (Dep::REQUIRES).begin(); it != item->dep (Dep::REQUIRES).end(); ++it)
00250           {
00251             requires.push_back(*it);
00252           }
00253       }
00254 
00255     for (CapList::const_iterator iter = requires.begin(); iter != requires.end(); ++iter)
00256     {
00257         const Capability requirement = *iter;
00258         XXX << "check requirement " << requirement << " of " << ITEMNAME(item) << endl;
00259         PoolItemList tovisit;
00260 
00261         // _world->foreachProvidingResItem (requirement, collect_providers, &info);
00262         Dep dep (Dep::PROVIDES);
00263 
00264         // first, look in _installed
00265         CollectProviders info ( item, _installed );
00266 
00267         invokeOnEach( _pool.byCapabilityIndexBegin( requirement.index(), dep ),
00268                       _pool.byCapabilityIndexEnd( requirement.index(), dep ),
00269                       resfilter::ByCapMatch( requirement ),
00270                       functor::functorRef<bool,CapAndItem>(info) );
00271 
00272         // if not found in _iustalled, look in _toinstall
00273 
00274         if (info.result.empty()) {
00275             CollectProviders info1 ( item, _toinstall );
00276 
00277             invokeOnEach( _pool.byCapabilityIndexBegin( requirement.index(), dep ),
00278                           _pool.byCapabilityIndexEnd( requirement.index(), dep ),
00279                           resfilter::ByCapMatch( requirement ),
00280                           functor::functorRef<bool,CapAndItem>(info1) );
00281 
00282             tovisit = info1.result;
00283         }
00284 
00285         for (PoolItemList::iterator it = tovisit.begin(); it != tovisit.end(); ++it)
00286         {
00287             const PoolItem_Ref must_visit = *it;
00288             if (_nodes[must_visit].visited == false)
00289             {
00290                 nodeinfo.order++;
00291                 _rgraph[must_visit].push_back( item );
00292                 _graph[item].push_back( must_visit );
00293                 rdfsvisit( must_visit );
00294             }
00295             else if (_nodes[must_visit].endtime == 0)
00296             {
00297                 if (must_visit != item)
00298                 {
00299                   // log only the 1st occurrence.
00300                   std::string lstr( ITEMNAME(item) );
00301                   lstr += " -> ";
00302                   lstr += ITEMNAME(must_visit);
00303                   if ( _logset.insert( lstr ).second )
00304                   {
00305                     WAR << "** dependency loop: " << lstr << endl;
00306                   }
00307                 }
00308             }
00309             else
00310             {
00311                 // filter multiple depends on same item (cosmetic)
00312                 PoolItemList & lrg = _rgraph[must_visit];
00313                 if( find( lrg.begin(), lrg.end(), item) == lrg.end() )
00314                 {
00315                     nodeinfo.order++;
00316                     lrg.push_back( item );
00317 
00318                     PoolItemList & lg = _graph[item];
00319                     if( find( lg.begin(), lg.end(), must_visit ) == lg.end() )
00320                         lg.push_back( must_visit );
00321                 }
00322             }
00323         }
00324     }
00325     _topsorted.push_back(item);
00326     _nodes[item].endtime = _rdfstime;
00327     _rdfstime++;
00328 
00329     XXX << ITEMNAME(item) << " done" << endl;
00330 }
00331 
00332 
00333 void
00334 InstallOrder::startrdfs()
00335 {
00336     _nodes.clear();
00337     _rgraph.clear();
00338     _graph.clear();
00339 
00340     _rdfstime = 1;
00341 
00342     _topsorted.clear();
00343 
00344     _numrun++;
00345     XXX << "run #" << _numrun << endl;
00346 
00347     // initialize all nodes
00348     for (PoolItemSet::iterator it = _toinstall.begin(); it != _toinstall.end(); ++it)
00349     {
00350         PoolItem_Ref item = *it;
00351         _nodes[item] = NodeInfo (item);
00352         _rgraph[item] = PoolItemList();
00353         _graph[item] = PoolItemList();
00354     }
00355 
00356     // visit all nodes
00357     for (PoolItemSet::iterator it = _toinstall.begin(); it != _toinstall.end(); ++it)
00358     {
00359         const PoolItem_Ref item = *it;
00360         if (_nodes[item].visited == false)
00361         {
00362             XXX << "start recursion on " << ITEMNAME(item) << endl;
00363             rdfsvisit (item);
00364         }
00365     }
00366 
00367     _dirty = false;
00368 }
00369 
00370 
00371 //-----------------------------------------------------------------------------
00372 
00373 const PoolItemList
00374 InstallOrder::getTopSorted() const
00375 {
00376     return _topsorted;
00377 }
00378 
00379 
00381     };// namespace detail
00384   };// namespace solver
00387 };// namespace zypp

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