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/solver/detail/SATResolver.h"
00036 
00037 #include "zypp/ResFilters.h"
00038 #include "zypp/ResStatus.h"
00039 #include "zypp/sat/Pool.h"
00040 #include <zypp/sat/WhatObsoletes.h>
00041 
00043 namespace zypp
00044 { 
00045 
00046   namespace solver
00047   { 
00048 
00049     namespace detail
00050     { 
00051 
00052 using namespace std;
00053 using namespace zypp;
00054 
00055 #define ITEMNAME(item) (item)->name()
00056 //-----------------------------------------------------------------------------
00057 
00058 InstallOrder::InstallOrder( const ResPool & pool, const PoolItemSet & toinstall, const PoolItemSet & installed )
00059     : _pool( pool )
00060     , _toinstall( toinstall )
00061     , _installed( installed )
00062     , _dirty (true)
00063     , _numrun (0)
00064 {
00065     _DEBUG("InstallOrder::InstallOrder(_toinstall " << _toinstall.size() << " items, _installed " << _installed.size() << " items)");
00066 }
00067 
00068 
00069 //-----------------------------------------------------------------------------
00070 
00071 void
00072 InstallOrder::printAdj (std::ostream& os, bool reversed) const
00073 {
00074     const Graph& g = (reversed ? _rgraph : _graph);
00075     os << "digraph pkgdeps {" << endl;
00076     for (Graph::const_iterator gcit = g.begin(); gcit != g.end(); ++gcit)
00077     {
00078         Nodes::const_iterator niit = _nodes.find(gcit->first);
00079         int order = niit->second.order;
00080         string name = gcit->first->name();
00081         os << "\"" << name << "\"" << "[label=\"" << name << "\\n" << order << "\"";
00082         os << "] " << endl;
00083         for (PoolItemList::const_iterator scit = gcit->second.begin(); scit != gcit->second.end(); ++scit)
00084         {
00085             os << "\"" << name << "\" -> \"" << (*scit)->name() << "\"" << endl;
00086         }
00087     }
00088     os << "}" << endl;
00089 }
00090 
00091 
00092 //-----------------------------------------------------------------------------
00093 
00094 // yea, that stuff is suboptimal. there should be a heap sorted by order
00095 PoolItemList
00096 InstallOrder::computeNextSet()
00097 {
00098     PoolItemList newlist;
00099 
00100     if (_dirty) startrdfs();
00101 
00102     for (Nodes::iterator it = _nodes.begin(); it != _nodes.end(); ++it)
00103     {
00104         if (it->second.order == 0
00105             && it->second.item)                 // the default Nodes constructor leaves this empty
00106         {
00107             XXX << "InstallOrder::computeNextSet found " << ITEMNAME(it->second.item) << endl;
00108 
00109             newlist.push_back(it->second.item);
00110         }
00111     }
00112 
00113     return newlist;
00114 }
00115 
00116 
00117 // decrease order of every adjacent node
00118 void
00119 InstallOrder::setInstalled(PoolItem item )
00120 {
00121     _dirty = true;
00122 
00123     PoolItemList adj = _rgraph[item];
00124 
00125     XXX << "InstallOrder::setInstalled " << ITEMNAME(item) << endl;
00126 
00127     // order will be < 0
00128     _nodes[item].order--;
00129     _installed.insert( item );
00130     _toinstall.erase( item );
00131 
00132     for (PoolItemList::iterator it = adj.begin(); it != adj.end(); ++it)
00133     {
00134         NodeInfo& info = _nodes[*it];
00135         info.order--;
00136         if (info.order < 0)
00137         {
00138             WAR << "order of node " << (*it) << " is < 0" << endl;
00139         }
00140     }
00141 }
00142 
00143 
00144 void
00145 InstallOrder::setInstalled( const PoolItemList & rl )
00146 {
00147     for (PoolItemList::const_iterator it = rl.begin(); it != rl.end(); ++it)
00148     {
00149         setInstalled(*it);
00150     }
00151 }
00152 
00153 //-----------------------------------------------------------------------------
00154 
00155 bool
00156 InstallOrder::doesProvide( const Capability requirement, PoolItem item ) const
00157 {
00158     Capabilities::const_iterator pend = item->dep( Dep::PROVIDES ).end();
00159     for( Capabilities::const_iterator pit = item->dep( Dep::PROVIDES ).begin(); pit != pend; ++pit) {
00160         if( pit->matches( requirement ) == CapMatch::yes ) {
00161             return item;
00162         }
00163     }
00164     return PoolItem();
00165 }
00166 
00167 
00168 PoolItem
00169 InstallOrder::findProviderInSet( const Capability requirement, const PoolItemSet & candidates ) const
00170 {
00171     for( PoolItemSet::const_iterator citer = candidates.begin(); citer != candidates.end(); citer++) {
00172         if( doesProvide( requirement, *citer ) ) {
00173             return *citer;
00174         }
00175     }
00176 
00177     return PoolItem();
00178 }
00179 
00180 //-----------------------------------------------------------------------------
00181 
00182 
00183 void
00184 InstallOrder::rdfsvisit (const PoolItem item)
00185 {
00186     typedef list<Capability> CapList;
00187     CapList requires;
00188 
00189     XXX << "InstallOrder::rdfsvisit, visiting " << ITEMNAME(item) << endl;
00190 
00191     NodeInfo& nodeinfo = _nodes[item];
00192 
00193     nodeinfo.visited = true;
00194     nodeinfo.begintime = _rdfstime;
00195     _rdfstime++;
00196 
00197     // items prereq
00198     CapabilitySet prq( item->dep(Dep::PREREQUIRES).begin(), item->dep(Dep::PREREQUIRES).end() );
00199     // an installed items prereq (in case they are reqired for uninstall scripts)
00200     ui::Selectable::Ptr sel( ui::Selectable::get( item ) );
00201     for_( it, sel->installedBegin(), sel->installedEnd() )
00202     {
00203       Capabilities p( it->satSolvable().prerequires() );
00204       prq.insert( p.begin(), p.end() );
00205     }
00206     // any obsoleted items prereq (in case they are reqired for uninstall scripts)
00207     sat::WhatObsoletes obs( item );
00208     for_( it, obs.begin(), obs.end() )
00209     {
00210       Capabilities p( it->prerequires() );
00211       prq.insert( p.begin(), p.end() );
00212     }
00213     // put prerequires first and requires last on list to ensure
00214     // that prerequires are processed first
00215     for (CapabilitySet::const_iterator it = prq.begin(); it != prq.end(); ++it)
00216     {
00217         requires.push_back(*it);
00218     }
00219 
00220     // Product requirements are ignored to assert Product gets installed
00221     // as early as possible. Some stuff depends on it (e.g. registration).
00222     if ( ! isKind<Product>( item.resolvable() ) )
00223       {
00224         for (Capabilities::const_iterator it = item->dep (Dep::REQUIRES).begin(); it != item->dep (Dep::REQUIRES).end(); ++it)
00225           {
00226             requires.push_back(*it);
00227           }
00228       }
00229 
00230     for (CapList::const_iterator iter = requires.begin(); iter != requires.end(); ++iter)
00231     {
00232         bool goBack = false;
00233         const Capability requirement = *iter;
00234         PoolItemList providers;
00235 
00236         XXX << "check requirement " << requirement << " of " << ITEMNAME(item) << endl;
00237         SATResolver satResolver(_pool, sat::Pool::instance().get());
00238         PoolItemList tovisit;
00239         sat::WhatProvides possibleProviders(requirement);
00240 
00241         // first, look in _installed
00242         for_( iter, possibleProviders.begin(), possibleProviders.end() ) {
00243             PoolItem provider = ResPool::instance().find( *iter );
00244             if ( provider == item )
00245             {
00246               goBack = true;
00247               break;
00248             }
00249             if (_installed.find( provider ) != _installed.end())        // and is not installed
00250             {
00251                 XXX << "tovisit " << ITEMNAME(provider) << endl;
00252                 providers.push_back (provider);
00253             }
00254         }
00255 
00256         if ( goBack )
00257           continue;
00258 
00259         // if not found in _installed, look in _toinstall
00260 
00261         if (providers.empty()) {
00262             for_( iter, possibleProviders.begin(), possibleProviders.end() ) {
00263                 PoolItem provider = ResPool::instance().find( *iter );
00264                 if ((provider.resolvable() != item.resolvable())                // resolvable could provide its own requirement
00265                     && (_toinstall.find( provider ) != _toinstall.end()))       // and is not to be installed
00266                 {
00267                     XXX << "tovisit " << ITEMNAME(provider) << endl;
00268                     tovisit.push_back (provider);
00269                 }
00270             }
00271         }
00272 
00273         for (PoolItemList::iterator it = tovisit.begin(); it != tovisit.end(); ++it)
00274         {
00275             const PoolItem must_visit = *it;
00276             if (_nodes[must_visit].visited == false)
00277             {
00278                 nodeinfo.order++;
00279                 _rgraph[must_visit].push_back( item );
00280                 _graph[item].push_back( must_visit );
00281                 rdfsvisit( must_visit );
00282             }
00283             else if (_nodes[must_visit].endtime == 0)
00284             {
00285                 if (must_visit != item)
00286                 {
00287                   // log only the 1st occurrence.
00288                   std::string lstr( ITEMNAME(item) );
00289                   lstr += " -> ";
00290                   lstr += ITEMNAME(must_visit);
00291                   if ( _logset.insert( lstr ).second )
00292                   {
00293                     WAR << "** dependency loop: " << lstr << endl;
00294                   }
00295                 }
00296             }
00297             else
00298             {
00299                 // filter multiple depends on same item (cosmetic)
00300                 PoolItemList & lrg = _rgraph[must_visit];
00301                 if( find( lrg.begin(), lrg.end(), item) == lrg.end() )
00302                 {
00303                     nodeinfo.order++;
00304                     lrg.push_back( item );
00305 
00306                     PoolItemList & lg = _graph[item];
00307                     if( find( lg.begin(), lg.end(), must_visit ) == lg.end() )
00308                         lg.push_back( must_visit );
00309                 }
00310             }
00311         }
00312     }
00313     _topsorted.push_back(item);
00314     _nodes[item].endtime = _rdfstime;
00315     _rdfstime++;
00316 
00317     XXX << ITEMNAME(item) << " done" << endl;
00318 }
00319 
00320 
00321 void
00322 InstallOrder::startrdfs()
00323 {
00324     _nodes.clear();
00325     _rgraph.clear();
00326     _graph.clear();
00327 
00328     _rdfstime = 1;
00329 
00330     _topsorted.clear();
00331 
00332     _numrun++;
00333     XXX << "run #" << _numrun << endl;
00334 
00335     // initialize all nodes
00336     for (PoolItemSet::iterator it = _toinstall.begin(); it != _toinstall.end(); ++it)
00337     {
00338         PoolItem item = *it;
00339         _nodes[item] = NodeInfo (item);
00340         _rgraph[item] = PoolItemList();
00341         _graph[item] = PoolItemList();
00342     }
00343 
00344     // visit all nodes
00345     for (PoolItemSet::iterator it = _toinstall.begin(); it != _toinstall.end(); ++it)
00346     {
00347         const PoolItem item = *it;
00348         if (_nodes[item].visited == false)
00349         {
00350             XXX << "start recursion on " << ITEMNAME(item) << endl;
00351             rdfsvisit (item);
00352         }
00353     }
00354 
00355     _dirty = false;
00356 }
00357 
00358 
00359 //-----------------------------------------------------------------------------
00360 
00361 const PoolItemList
00362 InstallOrder::getTopSorted() const
00363 {
00364     return _topsorted;
00365 }
00366 
00367 
00369     };// namespace detail
00372   };// namespace solver
00375 };// namespace zypp

doxygen