0ca4d492c8376784c7274b111896897aee1e94cb
[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/solver/detail/SATResolver.h"
36
37 #include "zypp/ResFilters.h"
38 #include "zypp/ResStatus.h"
39 #include "zypp/NameKindProxy.h"
40 #include "zypp/sat/Pool.h"
41
42 /////////////////////////////////////////////////////////////////////////
43 namespace zypp
44 { ///////////////////////////////////////////////////////////////////////
45   ///////////////////////////////////////////////////////////////////////
46   namespace solver
47   { /////////////////////////////////////////////////////////////////////
48     /////////////////////////////////////////////////////////////////////
49     namespace detail
50     { ///////////////////////////////////////////////////////////////////
51
52 using namespace std;
53 using namespace zypp;
54
55 #define ITEMNAME(item) (item)->name()
56 //-----------------------------------------------------------------------------
57
58 InstallOrder::InstallOrder( const ResPool & pool, const PoolItemSet & toinstall, const PoolItemSet & installed )
59     : _pool( pool )
60     , _toinstall( toinstall )
61     , _installed( installed )
62     , _dirty (true)
63     , _numrun (0)
64 {
65     _DEBUG("InstallOrder::InstallOrder(_toinstall " << _toinstall.size() << " items, _installed " << _installed.size() << " items)");
66 }
67
68
69 //-----------------------------------------------------------------------------
70
71 void
72 InstallOrder::printAdj (std::ostream& os, bool reversed) const
73 {
74     const Graph& g = (reversed ? _rgraph : _graph);
75     os << "digraph pkgdeps {" << endl;
76     for (Graph::const_iterator gcit = g.begin(); gcit != g.end(); ++gcit)
77     {
78         Nodes::const_iterator niit = _nodes.find(gcit->first);
79         int order = niit->second.order;
80         string name = gcit->first->name();
81         os << "\"" << name << "\"" << "[label=\"" << name << "\\n" << order << "\"";
82         os << "] " << endl;
83         for (PoolItemList::const_iterator scit = gcit->second.begin(); scit != gcit->second.end(); ++scit)
84         {
85             os << "\"" << name << "\" -> \"" << (*scit)->name() << "\"" << endl;
86         }
87     }
88     os << "}" << endl;
89 }
90
91
92 //-----------------------------------------------------------------------------
93
94 // yea, that stuff is suboptimal. there should be a heap sorted by order
95 PoolItemList
96 InstallOrder::computeNextSet()
97 {
98     PoolItemList newlist;
99
100     if (_dirty) startrdfs();
101
102     for (Nodes::iterator it = _nodes.begin(); it != _nodes.end(); ++it)
103     {
104         if (it->second.order == 0
105             && it->second.item)                 // the default Nodes constructor leaves this empty
106         {
107             XXX << "InstallOrder::computeNextSet found " << ITEMNAME(it->second.item) << endl;
108
109             newlist.push_back(it->second.item);
110         }
111     }
112
113     return newlist;
114 }
115
116
117 // decrease order of every adjacent node
118 void
119 InstallOrder::setInstalled(PoolItem item )
120 {
121     _dirty = true;
122
123     PoolItemList adj = _rgraph[item];
124
125     XXX << "InstallOrder::setInstalled " << ITEMNAME(item) << endl;
126
127     // order will be < 0
128     _nodes[item].order--;
129     _installed.insert( item );
130     _toinstall.erase( item );
131
132     for (PoolItemList::iterator it = adj.begin(); it != adj.end(); ++it)
133     {
134         NodeInfo& info = _nodes[*it];
135         info.order--;
136         if (info.order < 0)
137         {
138             WAR << "order of node " << (*it) << " is < 0" << endl;
139         }
140     }
141 }
142
143
144 void
145 InstallOrder::setInstalled( const PoolItemList & rl )
146 {
147     for (PoolItemList::const_iterator it = rl.begin(); it != rl.end(); ++it)
148     {
149         setInstalled(*it);
150     }
151 }
152
153 //-----------------------------------------------------------------------------
154
155 bool
156 InstallOrder::doesProvide( const Capability requirement, PoolItem item ) const
157 {
158     Capabilities::const_iterator pend = item->dep( Dep::PROVIDES ).end();
159     for( Capabilities::const_iterator pit = item->dep( Dep::PROVIDES ).begin(); pit != pend; ++pit) {
160         if( pit->matches( requirement ) == CapMatch::yes ) {
161             return item;
162         }
163     }
164     return PoolItem();
165 }
166
167
168 PoolItem
169 InstallOrder::findProviderInSet( const Capability requirement, const PoolItemSet & candidates ) const
170 {
171     for( PoolItemSet::const_iterator citer = candidates.begin(); citer != candidates.end(); citer++) {
172         if( doesProvide( requirement, *citer ) ) {
173             return *citer;
174         }
175     }
176
177     return PoolItem();
178 }
179
180 //-----------------------------------------------------------------------------
181
182
183 void
184 InstallOrder::rdfsvisit (const PoolItem item)
185 {
186     typedef list<Capability> CapList;
187     CapList requires;
188
189     XXX << "InstallOrder::rdfsvisit, visiting " << ITEMNAME(item) << endl;
190
191     NodeInfo& nodeinfo = _nodes[item];
192
193     nodeinfo.visited = true;
194     nodeinfo.begintime = _rdfstime;
195     _rdfstime++;
196
197     // items prereq
198     CapabilitySet prq( item->dep(Dep::PREREQUIRES).begin(), item->dep(Dep::PREREQUIRES).end() );
199     // an installed items prereq (in case they are reqired for uninstall scripts)
200     NameKindProxy nkp( _pool, item->name(), item->kind() );
201     if ( ! nkp.installedEmpty() )
202     {
203       prq.insert( (*nkp.installedBegin())->dep(Dep::PREREQUIRES).begin(),
204                   (*nkp.installedBegin())->dep(Dep::PREREQUIRES).end() );
205     }
206     // put prerequires first and requires last on list to ensure
207     // that prerequires are processed first
208     for (CapabilitySet::const_iterator it = prq.begin(); it != prq.end(); ++it)
209     {
210         requires.push_back(*it);
211     }
212
213     // Product requirements are ignored to assert Product gets installed
214     // as early as possible. Some stuff depends on it (e.g. registration).
215     if ( ! isKind<Product>( item.resolvable() ) )
216       {
217         for (Capabilities::const_iterator it = item->dep (Dep::REQUIRES).begin(); it != item->dep (Dep::REQUIRES).end(); ++it)
218           {
219             requires.push_back(*it);
220           }
221       }
222
223     for (CapList::const_iterator iter = requires.begin(); iter != requires.end(); ++iter)
224     {
225         bool goBack = false;
226         const Capability requirement = *iter;
227         PoolItemList providers;
228
229         XXX << "check requirement " << requirement << " of " << ITEMNAME(item) << endl;
230         SATResolver satResolver(_pool, sat::Pool::instance().get());
231         PoolItemList tovisit;
232         sat::WhatProvides possibleProviders(requirement);
233
234         // first, look in _installed
235         for_( iter, possibleProviders.begin(), possibleProviders.end() ) {
236             PoolItem provider = ResPool::instance().find( *iter );
237             if ( provider == item )
238             {
239               goBack = true;
240               break;
241             }
242             if (_installed.find( provider ) != _installed.end())        // and is not installed
243             {
244                 XXX << "tovisit " << ITEMNAME(provider) << endl;
245                 providers.push_back (provider);
246             }
247         }
248
249         if ( goBack )
250           continue;
251
252         // if not found in _installed, look in _toinstall
253
254         if (providers.empty()) {
255             for_( iter, possibleProviders.begin(), possibleProviders.end() ) {
256                 PoolItem provider = ResPool::instance().find( *iter );
257                 if ((provider.resolvable() != item.resolvable())                // resolvable could provide its own requirement
258                     && (_toinstall.find( provider ) != _toinstall.end()))       // and is not to be installed
259                 {
260                     XXX << "tovisit " << ITEMNAME(provider) << endl;
261                     tovisit.push_back (provider);
262                 }
263             }
264         }
265
266         for (PoolItemList::iterator it = tovisit.begin(); it != tovisit.end(); ++it)
267         {
268             const PoolItem must_visit = *it;
269             if (_nodes[must_visit].visited == false)
270             {
271                 nodeinfo.order++;
272                 _rgraph[must_visit].push_back( item );
273                 _graph[item].push_back( must_visit );
274                 rdfsvisit( must_visit );
275             }
276             else if (_nodes[must_visit].endtime == 0)
277             {
278                 if (must_visit != item)
279                 {
280                   // log only the 1st occurrence.
281                   std::string lstr( ITEMNAME(item) );
282                   lstr += " -> ";
283                   lstr += ITEMNAME(must_visit);
284                   if ( _logset.insert( lstr ).second )
285                   {
286                     WAR << "** dependency loop: " << lstr << endl;
287                   }
288                 }
289             }
290             else
291             {
292                 // filter multiple depends on same item (cosmetic)
293                 PoolItemList & lrg = _rgraph[must_visit];
294                 if( find( lrg.begin(), lrg.end(), item) == lrg.end() )
295                 {
296                     nodeinfo.order++;
297                     lrg.push_back( item );
298
299                     PoolItemList & lg = _graph[item];
300                     if( find( lg.begin(), lg.end(), must_visit ) == lg.end() )
301                         lg.push_back( must_visit );
302                 }
303             }
304         }
305     }
306     _topsorted.push_back(item);
307     _nodes[item].endtime = _rdfstime;
308     _rdfstime++;
309
310     XXX << ITEMNAME(item) << " done" << endl;
311 }
312
313
314 void
315 InstallOrder::startrdfs()
316 {
317     _nodes.clear();
318     _rgraph.clear();
319     _graph.clear();
320
321     _rdfstime = 1;
322
323     _topsorted.clear();
324
325     _numrun++;
326     XXX << "run #" << _numrun << endl;
327
328     // initialize all nodes
329     for (PoolItemSet::iterator it = _toinstall.begin(); it != _toinstall.end(); ++it)
330     {
331         PoolItem item = *it;
332         _nodes[item] = NodeInfo (item);
333         _rgraph[item] = PoolItemList();
334         _graph[item] = PoolItemList();
335     }
336
337     // visit all nodes
338     for (PoolItemSet::iterator it = _toinstall.begin(); it != _toinstall.end(); ++it)
339     {
340         const PoolItem item = *it;
341         if (_nodes[item].visited == false)
342         {
343             XXX << "start recursion on " << ITEMNAME(item) << endl;
344             rdfsvisit (item);
345         }
346     }
347
348     _dirty = false;
349 }
350
351
352 //-----------------------------------------------------------------------------
353
354 const PoolItemList
355 InstallOrder::getTopSorted() const
356 {
357     return _topsorted;
358 }
359
360
361 ///////////////////////////////////////////////////////////////////
362     };// namespace detail
363     /////////////////////////////////////////////////////////////////////
364     /////////////////////////////////////////////////////////////////////
365   };// namespace solver
366   ///////////////////////////////////////////////////////////////////////
367   ///////////////////////////////////////////////////////////////////////
368 };// namespace zypp
369 /////////////////////////////////////////////////////////////////////////