back to previous version
[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/Logger.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
38 /////////////////////////////////////////////////////////////////////////
39 namespace zypp
40 { ///////////////////////////////////////////////////////////////////////
41   ///////////////////////////////////////////////////////////////////////
42   namespace solver
43   { /////////////////////////////////////////////////////////////////////
44     /////////////////////////////////////////////////////////////////////
45     namespace detail
46     { ///////////////////////////////////////////////////////////////////
47
48 using namespace std;
49 using namespace zypp;
50
51 //-----------------------------------------------------------------------------
52
53 InstallOrder::InstallOrder(const ResPool & pool, const PoolItemList & toinstall, const PoolItemList & installed)
54     : _pool (pool)
55     , _toinstall (toinstall.begin(), toinstall.end())
56     , _installed (installed.begin(), installed.end())
57     , _dirty (true)
58     , _numrun (0)
59 {
60     _DEBUG("InstallOrder::InstallOrder(_toinstall " << _toinstall.size() << " items, _installed " << _installed.size() << " items)");
61 }
62
63
64 //-----------------------------------------------------------------------------
65
66 const void
67 InstallOrder::printAdj (std::ostream& os, bool reversed) const
68 {
69     const Graph& g = (reversed ? _rgraph : _graph);
70     os << "digraph pkgdeps {" << endl;
71     for (Graph::const_iterator gcit = g.begin(); gcit != g.end(); ++gcit)
72     {
73         Nodes::const_iterator niit = _nodes.find(gcit->first);
74         int order = niit->second.order;
75         string name = gcit->first.resolvable()->name();
76         os << "\"" << name << "\"" << "[label=\"" << name << "\\n" << order << "\"";
77         os << "] " << endl;
78         for (PoolItemSet::const_iterator scit = gcit->second.begin(); scit != gcit->second.end(); ++scit)
79         {
80             os << "\"" << name << "\" -> \"" << (*scit).resolvable()->name() << "\"" << endl;
81         }
82     }
83     os << "}" << endl;
84 }
85
86
87 //-----------------------------------------------------------------------------
88
89 // yea, that stuff is suboptimal. there should be a heap sorted by order
90 PoolItemList
91 InstallOrder::computeNextSet()
92 {
93     PoolItemList newlist;
94
95     if (_dirty) startrdfs();
96
97     for (Nodes::iterator it = _nodes.begin(); it != _nodes.end(); ++it)
98     {
99         if (it->second.order == 0
100             && it->second.item)                 // the default Nodes constructor leaves this empty 
101         {
102             DBG << "InstallOrder::computeNextSet found " << it->second.item << endl;
103
104             newlist.push_back(it->second.item);
105         }
106     }
107
108     return newlist;
109 }
110
111
112 // decrease order of every adjacent node
113 void
114 InstallOrder::setInstalled(PoolItem_Ref item )
115 {
116     _dirty = true;
117
118     PoolItemSet adj = _rgraph[item];
119
120     DBG << "InstallOrder::setInstalled " << item << endl;
121
122     // order will be < 0
123     _nodes[item].order--;
124     _installed.insert (item);
125     _toinstall.erase (item);
126
127     for (PoolItemSet::iterator it = adj.begin(); it != adj.end(); ++it)
128     {
129         NodeInfo& info = _nodes[*it];
130         info.order--;
131         if (info.order < 0)
132         {
133             DBG << "order of node " << (*it) << " is < 0" << endl;
134         }
135     }
136 }
137
138
139 void
140 InstallOrder::setInstalled( const PoolItemList & rl )
141 {
142     for (PoolItemList::const_iterator it = rl.begin(); it != rl.end(); ++it)
143     {
144         setInstalled(*it);
145     }
146 }
147
148 //-----------------------------------------------------------------------------
149
150
151 void
152 InstallOrder::startrdfs()
153 {
154     _nodes.clear();
155     _rgraph.clear();
156     _graph.clear();
157
158     _rdfstime = 1;
159
160     _topsorted.clear();
161
162     _numrun++;
163     DBG << "run #" << _numrun << endl;
164
165     // initialize all nodes
166     for (PoolItemSet::iterator it = _toinstall.begin(); it != _toinstall.end(); ++it)
167     {
168         PoolItem_Ref item = *it;
169         _nodes[item] = NodeInfo (item);
170         _rgraph[item] = PoolItemSet();
171         _graph[item] = PoolItemSet();
172     }
173
174     // visit all nodes
175     for (PoolItemSet::iterator it = _toinstall.begin(); it != _toinstall.end(); ++it)
176     {
177         const PoolItem_Ref item = *it;
178         if (_nodes[item].visited == false)
179         {
180             DBG << "start recursion on " << item << endl;
181             rdfsvisit (item);
182         }
183     }
184
185     _dirty = false;
186 }
187
188
189
190
191 struct CollectProviders : public resfilter::OnCapMatchCallbackFunctor
192 {
193     const PoolItem_Ref requestor;
194     PoolItemSet & tovisit;   
195     PoolItemSet & toinstall;
196     PoolItemSet & installed;
197
198     CollectProviders (const PoolItem_Ref pi, PoolItemSet & tv, PoolItemSet & ti, PoolItemSet i)
199         : requestor (pi)
200         , tovisit (tv)
201         , toinstall (ti)
202         , installed (i)
203     { }
204
205
206     bool operator()( PoolItem_Ref provider, const Capability & match )
207     {
208         // item provides cap which matches a requirement from info->requestor
209         //   this function gets _all_ providers and filter out those which are
210         //   either installed or in our toinstall input list
211         //
212
213         if ((provider.resolvable() != requestor.resolvable())           // resolvable could provide its own requirement
214             && (!provider.status().staysInstalled())                    // only visit if provider is not already installed
215             && (toinstall.find(provider) != toinstall.end()             // only look at resolvables
216                 || installed.find(provider) != installed.end())) {      //   we are currently considering anyways
217             tovisit.insert (provider);
218         }
219
220         return true;
221     }
222
223 };
224
225
226 void
227 InstallOrder::rdfsvisit (const PoolItem_Ref  item)
228 {
229     typedef list<Capability> CapList;
230     CapList requires;
231
232     DBG << "InstallOrder::rdfsvisit, visiting " << item << endl;
233
234     NodeInfo& nodeinfo = _nodes[item];
235
236     nodeinfo.visited = true;
237     nodeinfo.begintime = _rdfstime;
238     _rdfstime++;
239
240     // put prerequires first and requires last on list to ensure
241     // that prerequires are processed first
242
243     for (CapSet::const_iterator it = item->dep (Dep::PREREQUIRES).begin(); it != item->dep (Dep::PREREQUIRES).end(); ++it)
244     {
245         const Capability cap = *it;
246         requires.push_back(cap);
247     }
248
249     for (CapSet::const_iterator it = item->dep (Dep::REQUIRES).begin(); it != item->dep (Dep::REQUIRES).end(); ++it)
250     {
251         const Capability cap = *it;
252         requires.push_back(cap);
253     }
254
255     for (CapList::const_iterator iter = requires.begin(); iter != requires.end(); ++iter)
256     {
257         const Capability requirement = *iter;
258         XXX << "check requirement " << requirement << " of " << item << endl;
259         PoolItemSet tovisit;
260
261         CollectProviders info ( item, tovisit, _toinstall, _installed );
262
263
264         // _world->foreachProvidingResItem (requirement, collect_providers, &info);
265 #if 0
266         Dep dep (Dep::PROVIDES);
267         invokeOnEach( _pool.byCapabilityIndexBegin( requirement.index(), dep ),
268                       _pool.byCapabilityIndexEnd( requirement.index(), dep ),
269                       resfilter::callOnCapMatchIn( dep, requirement, functor::functorRef<bool,PoolItem,Capability>(info) ) );
270 #endif
271         ResPool::const_indexiterator pend = _pool.providesend(requirement.index());
272         for (ResPool::const_indexiterator it = _pool.providesbegin(requirement.index()); it != pend; ++it) {
273             if (it->second.second->arch() == Arch_src)
274                 continue;
275             if (requirement.matches (it->second.first) == CapMatch::yes) {
276                 if (!info( it->second.second, it->second.first))
277                     break;
278             }
279         }
280
281         for (PoolItemSet::iterator it = tovisit.begin(); it != tovisit.end(); ++it)
282         {
283             const PoolItem_Ref must_visit = *it;
284             if (_nodes[must_visit].visited == false)
285             {
286                 nodeinfo.order++;
287                 _rgraph[must_visit].insert (item);
288                 _graph[item].insert (must_visit);
289                 rdfsvisit(must_visit);
290             }
291             else if (_nodes[must_visit].endtime == 0)
292             {
293                 if (must_visit != item)
294                 {
295                     ERR << "*************************************************************" << endl;
296                     ERR << "** dependency loop: " << item << " -> " << must_visit << endl;
297                     ERR << "*************************************************************" << endl;
298                 }
299             }
300             else
301             {
302                 // filter multiple depends on same item (cosmetic)
303                 PoolItemSet & lrg = _rgraph[must_visit];
304                 if (lrg.find(item) == lrg.end())
305                 {
306                     nodeinfo.order++;
307                     lrg.insert(item);
308
309                     PoolItemSet & lg = _graph[item];
310                     if (lg.find (must_visit) == lg.end())
311                         lg.insert (must_visit);
312                 }
313             }
314         }
315     }
316     _topsorted.push_back(item);
317     _nodes[item].endtime = _rdfstime;
318     _rdfstime++;
319
320     DBG << item << " done" << endl;
321 }
322
323
324 //-----------------------------------------------------------------------------
325
326 const PoolItemList
327 InstallOrder::getTopSorted() const
328 {
329     return _topsorted;
330 }
331
332
333 ///////////////////////////////////////////////////////////////////
334     };// namespace detail
335     /////////////////////////////////////////////////////////////////////
336     /////////////////////////////////////////////////////////////////////
337   };// namespace solver
338   ///////////////////////////////////////////////////////////////////////
339   ///////////////////////////////////////////////////////////////////////
340 };// namespace zypp
341 /////////////////////////////////////////////////////////////////////////