removing old solver
[platform/upstream/libzypp.git] / zypp / solver / detail / InstallOrder.cc
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* InstallOrder.cc
3  *
4  * Copyright (C) 2005 SUSE Linux Products GmbH
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License,
8  * version 2, as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18  * 02111-1307, USA.
19  */
20
21 // stolen from yast2-packagemanager
22 /*
23    File:       InstallOrder.h
24    Purpose:    Determine order for installing packages
25    Author:     Ludwig Nussel <lnussel@suse.de>
26    Maintainer: Ludwig Nussel <lnussel@suse.de>
27
28 /-*/
29
30 #include "zypp/solver/detail/InstallOrder.h"
31 #include "zypp/base/LogTools.h"
32 #include "zypp/base/Iterator.h"
33 #include "zypp/base/Algorithm.h"
34
35 #include "zypp/ResFilters.h"
36 #include "zypp/ResStatus.h"
37 #include "zypp/CapAndItem.h"
38 #include "zypp/NameKindProxy.h"
39
40 /////////////////////////////////////////////////////////////////////////
41 namespace zypp
42 { ///////////////////////////////////////////////////////////////////////
43   ///////////////////////////////////////////////////////////////////////
44   namespace solver
45   { /////////////////////////////////////////////////////////////////////
46     /////////////////////////////////////////////////////////////////////
47     namespace detail
48     { ///////////////////////////////////////////////////////////////////
49
50 using namespace std;
51 using namespace zypp;
52
53 #define ITEMNAME(item) (item)->name()
54 //-----------------------------------------------------------------------------
55
56 InstallOrder::InstallOrder( const ResPool & pool, const PoolItemSet & toinstall, const PoolItemSet & installed )
57     : _pool( pool )
58     , _toinstall( toinstall )
59     , _installed( installed )
60     , _dirty (true)
61     , _numrun (0)
62 {
63     _DEBUG("InstallOrder::InstallOrder(_toinstall " << _toinstall.size() << " items, _installed " << _installed.size() << " items)");
64 }
65
66
67 //-----------------------------------------------------------------------------
68
69 void
70 InstallOrder::printAdj (std::ostream& os, bool reversed) const
71 {
72     const Graph& g = (reversed ? _rgraph : _graph);
73     os << "digraph pkgdeps {" << endl;
74     for (Graph::const_iterator gcit = g.begin(); gcit != g.end(); ++gcit)
75     {
76         Nodes::const_iterator niit = _nodes.find(gcit->first);
77         int order = niit->second.order;
78         string name = gcit->first->name();
79         os << "\"" << name << "\"" << "[label=\"" << name << "\\n" << order << "\"";
80         os << "] " << endl;
81         for (PoolItemList::const_iterator scit = gcit->second.begin(); scit != gcit->second.end(); ++scit)
82         {
83             os << "\"" << name << "\" -> \"" << (*scit)->name() << "\"" << endl;
84         }
85     }
86     os << "}" << endl;
87 }
88
89
90 //-----------------------------------------------------------------------------
91
92 // yea, that stuff is suboptimal. there should be a heap sorted by order
93 PoolItemList
94 InstallOrder::computeNextSet()
95 {
96     PoolItemList newlist;
97
98     if (_dirty) startrdfs();
99
100     for (Nodes::iterator it = _nodes.begin(); it != _nodes.end(); ++it)
101     {
102         if (it->second.order == 0
103             && it->second.item)                 // the default Nodes constructor leaves this empty
104         {
105             XXX << "InstallOrder::computeNextSet found " << ITEMNAME(it->second.item) << endl;
106
107             newlist.push_back(it->second.item);
108         }
109     }
110
111     return newlist;
112 }
113
114
115 // decrease order of every adjacent node
116 void
117 InstallOrder::setInstalled(PoolItem_Ref item )
118 {
119     _dirty = true;
120
121     PoolItemList adj = _rgraph[item];
122
123     XXX << "InstallOrder::setInstalled " << ITEMNAME(item) << endl;
124
125     // order will be < 0
126     _nodes[item].order--;
127     _installed.insert( item );
128     _toinstall.erase( item );
129
130     for (PoolItemList::iterator it = adj.begin(); it != adj.end(); ++it)
131     {
132         NodeInfo& info = _nodes[*it];
133         info.order--;
134         if (info.order < 0)
135         {
136             WAR << "order of node " << (*it) << " is < 0" << endl;
137         }
138     }
139 }
140
141
142 void
143 InstallOrder::setInstalled( const PoolItemList & rl )
144 {
145     for (PoolItemList::const_iterator it = rl.begin(); it != rl.end(); ++it)
146     {
147         setInstalled(*it);
148     }
149 }
150
151 //-----------------------------------------------------------------------------
152
153 bool
154 InstallOrder::doesProvide( const Capability requirement, PoolItem_Ref item ) const
155 {
156     Capabilities::const_iterator pend = item->dep( Dep::PROVIDES ).end();
157     for( Capabilities::const_iterator pit = item->dep( Dep::PROVIDES ).begin(); pit != pend; ++pit) {
158         if( pit->matches( requirement ) == CapMatch::yes ) {
159             return item;
160         }
161     }
162     return PoolItem_Ref();
163 }
164
165
166 PoolItem_Ref
167 InstallOrder::findProviderInSet( const Capability requirement, const PoolItemSet & candidates ) const
168 {
169     for( PoolItemSet::const_iterator citer = candidates.begin(); citer != candidates.end(); citer++) {
170         if( doesProvide( requirement, *citer ) ) {
171             return *citer;
172         }
173     }
174
175     return PoolItem_Ref();
176 }
177
178 struct CollectProviders
179 {
180     const PoolItem_Ref requestor;
181     PoolItemList result;
182     const PoolItemSet & limitto;                // limit search to members of this set
183
184     CollectProviders (const PoolItem_Ref pi, const PoolItemSet & limit)
185         : requestor (pi)
186         , limitto (limit)
187     { }
188
189
190     bool operator()( const CapAndItem & c_and_i )
191     {
192         // item provides cap which matches a requirement from info->requestor
193         //   this function gets _all_ providers and filter out those which are
194         //   either installed or in our toinstall input list
195         //
196 XXX << "info(" << c_and_i.item <<")"<< endl;
197         if ((c_and_i.item.resolvable() != requestor.resolvable())       // resolvable could provide its own requirement
198             && (limitto.find( c_and_i.item ) != limitto.end()))         // limit to members of 'limitto' set
199         {
200             XXX << "tovisit " << ITEMNAME(c_and_i.item) << endl;
201             result.push_back (c_and_i.item);
202         }
203
204         return true;
205     }
206
207 };
208
209 //-----------------------------------------------------------------------------
210
211
212 void
213 InstallOrder::rdfsvisit (const PoolItem_Ref item)
214 {
215     typedef list<Capability> CapList;
216     CapList requires;
217
218     XXX << "InstallOrder::rdfsvisit, visiting " << ITEMNAME(item) << endl;
219
220     NodeInfo& nodeinfo = _nodes[item];
221
222     nodeinfo.visited = true;
223     nodeinfo.begintime = _rdfstime;
224     _rdfstime++;
225
226     // items prereq
227     Dependencies dependencies = item->deps();
228     CapabilitySet prq = dependencies[Dep::PREREQUIRES];
229     // an installed items prereq (in case they are reqired for uninstall scripts)
230     NameKindProxy nkp( _pool, item->name(), item->kind() );
231     if ( ! nkp.installedEmpty() )
232     {
233       prq.insert( (*nkp.installedBegin())->dep(Dep::PREREQUIRES).begin(),
234                   (*nkp.installedBegin())->dep(Dep::PREREQUIRES).end() );
235     }
236     // put prerequires first and requires last on list to ensure
237     // that prerequires are processed first
238     for (CapabilitySet::const_iterator it = prq.begin(); it != prq.end(); ++it)
239     {
240         requires.push_back(*it);
241     }
242
243     // Product requirements are ignored to assert Product gets installed
244     // as early as possible. Some stuff depends on it (e.g. registration).
245     if ( ! isKind<Product>( item.resolvable() ) )
246       {
247         for (Capabilities::const_iterator it = item->dep (Dep::REQUIRES).begin(); it != item->dep (Dep::REQUIRES).end(); ++it)
248           {
249             requires.push_back(*it);
250           }
251       }
252
253     for (CapList::const_iterator iter = requires.begin(); iter != requires.end(); ++iter)
254     {
255         const Capability requirement = *iter;
256         XXX << "check requirement " << requirement << " of " << ITEMNAME(item) << endl;
257         PoolItemList tovisit;
258
259         // _world->foreachProvidingResItem (requirement, collect_providers, &info);
260         Dep dep (Dep::PROVIDES);
261
262         // first, look in _installed
263         CollectProviders info ( item, _installed );
264
265         invokeOnEach( _pool.byCapabilityIndexBegin( requirement.index(), dep ),
266                       _pool.byCapabilityIndexEnd( requirement.index(), dep ),
267                       resfilter::ByCapMatch( requirement ),
268                       functor::functorRef<bool,CapAndItem>(info) );
269
270         // if not found in _iustalled, look in _toinstall
271
272         if (info.result.empty()) {
273             CollectProviders info1 ( item, _toinstall );
274
275             invokeOnEach( _pool.byCapabilityIndexBegin( requirement.index(), dep ),
276                           _pool.byCapabilityIndexEnd( requirement.index(), dep ),
277                           resfilter::ByCapMatch( requirement ),
278                           functor::functorRef<bool,CapAndItem>(info1) );
279
280             tovisit = info1.result;
281         }
282
283         for (PoolItemList::iterator it = tovisit.begin(); it != tovisit.end(); ++it)
284         {
285             const PoolItem_Ref must_visit = *it;
286             if (_nodes[must_visit].visited == false)
287             {
288                 nodeinfo.order++;
289                 _rgraph[must_visit].push_back( item );
290                 _graph[item].push_back( must_visit );
291                 rdfsvisit( must_visit );
292             }
293             else if (_nodes[must_visit].endtime == 0)
294             {
295                 if (must_visit != item)
296                 {
297                   // log only the 1st occurrence.
298                   std::string lstr( ITEMNAME(item) );
299                   lstr += " -> ";
300                   lstr += ITEMNAME(must_visit);
301                   if ( _logset.insert( lstr ).second )
302                   {
303                     WAR << "** dependency loop: " << lstr << endl;
304                   }
305                 }
306             }
307             else
308             {
309                 // filter multiple depends on same item (cosmetic)
310                 PoolItemList & lrg = _rgraph[must_visit];
311                 if( find( lrg.begin(), lrg.end(), item) == lrg.end() )
312                 {
313                     nodeinfo.order++;
314                     lrg.push_back( item );
315
316                     PoolItemList & lg = _graph[item];
317                     if( find( lg.begin(), lg.end(), must_visit ) == lg.end() )
318                         lg.push_back( must_visit );
319                 }
320             }
321         }
322     }
323     _topsorted.push_back(item);
324     _nodes[item].endtime = _rdfstime;
325     _rdfstime++;
326
327     XXX << ITEMNAME(item) << " done" << endl;
328 }
329
330
331 void
332 InstallOrder::startrdfs()
333 {
334     _nodes.clear();
335     _rgraph.clear();
336     _graph.clear();
337
338     _rdfstime = 1;
339
340     _topsorted.clear();
341
342     _numrun++;
343     XXX << "run #" << _numrun << endl;
344
345     // initialize all nodes
346     for (PoolItemSet::iterator it = _toinstall.begin(); it != _toinstall.end(); ++it)
347     {
348         PoolItem_Ref item = *it;
349         _nodes[item] = NodeInfo (item);
350         _rgraph[item] = PoolItemList();
351         _graph[item] = PoolItemList();
352     }
353
354     // visit all nodes
355     for (PoolItemSet::iterator it = _toinstall.begin(); it != _toinstall.end(); ++it)
356     {
357         const PoolItem_Ref item = *it;
358         if (_nodes[item].visited == false)
359         {
360             XXX << "start recursion on " << ITEMNAME(item) << endl;
361             rdfsvisit (item);
362         }
363     }
364
365     _dirty = false;
366 }
367
368
369 //-----------------------------------------------------------------------------
370
371 const PoolItemList
372 InstallOrder::getTopSorted() const
373 {
374     return _topsorted;
375 }
376
377
378 ///////////////////////////////////////////////////////////////////
379     };// namespace detail
380     /////////////////////////////////////////////////////////////////////
381     /////////////////////////////////////////////////////////////////////
382   };// namespace solver
383   ///////////////////////////////////////////////////////////////////////
384   ///////////////////////////////////////////////////////////////////////
385 };// namespace zypp
386 /////////////////////////////////////////////////////////////////////////