* Gives information about WHICH items require an already installed
[platform/upstream/libzypp.git] / zypp / solver / detail / Resolver.cc
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* Resolver.cc
3  *
4  * Copyright (C) 2000-2002 Ximian, Inc.
5  * Copyright (C) 2005 SUSE Linux Products GmbH
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License,
9  * version 2, as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19  * 02111-1307, USA.
20  */
21 #include <boost/static_assert.hpp>
22
23 #include "zypp/solver/detail/Resolver.h"
24 #include "zypp/solver/detail/Helper.h"
25 #include "zypp/solver/detail/Testcase.h"
26 #include "zypp/solver/detail/SATResolver.h"
27
28 #include "zypp/Capabilities.h"
29 #include "zypp/ZConfig.h"
30 #include "zypp/base/Logger.h"
31 #include "zypp/base/String.h"
32 #include "zypp/base/Gettext.h"
33 #include "zypp/base/Algorithm.h"
34 #include "zypp/ResPool.h"
35 #include "zypp/ResFilters.h"
36 #include "zypp/sat/Pool.h"
37 #include "zypp/sat/Solvable.h"
38
39 #define MAXSOLVERRUNS 5
40
41 /////////////////////////////////////////////////////////////////////////
42 namespace zypp
43 { ///////////////////////////////////////////////////////////////////////
44   ///////////////////////////////////////////////////////////////////////
45   namespace solver
46   { /////////////////////////////////////////////////////////////////////
47     /////////////////////////////////////////////////////////////////////
48     namespace detail
49     { ///////////////////////////////////////////////////////////////////
50
51 using namespace std;
52
53 IMPL_PTR_TYPE(Resolver);
54
55
56 //---------------------------------------------------------------------------
57
58
59 std::ostream &
60 Resolver::dumpOn( std::ostream & os ) const
61 {
62     return os << "<resolver/>";
63 }
64
65
66 //---------------------------------------------------------------------------
67
68 Resolver::Resolver (const ResPool & pool)
69     : _pool(pool)
70     , _satResolver(NULL)
71     , _poolchanged(_pool.serial() )
72     , _testing(false)
73     , _forceResolve(false)
74     , _upgradeMode(false)
75     , _verifying(false)
76     , _onlyRequires(indeterminate)
77     , _ignorealreadyrecommended(false)
78
79 {
80     sat::Pool satPool( sat::Pool::instance() );
81     _satResolver = new SATResolver(_pool, satPool.get());
82 }
83
84
85 Resolver::~Resolver()
86 {
87 }
88
89 //---------------------------------------------------------------------------
90
91 ResPool
92 Resolver::pool (void) const
93 {
94     return _pool;
95 }
96
97 void
98 Resolver::reset (bool keepExtras )
99 {
100     _verifying = false;    
101
102     if (!keepExtras) {
103       _extra_requires.clear();
104       _extra_conflicts.clear();
105     }
106
107     _isInstalledBy.clear();
108     _installs.clear();
109     _satifiedByInstalled.clear();
110     _installedSatisfied.clear();
111 }
112
113 void
114 Resolver::doUpdate()
115 {
116     return _satResolver->doUpdate();
117 }
118
119 void
120 Resolver::addExtraRequire (const Capability & capability)
121 {
122     _extra_requires.insert (capability);
123 }
124
125 void
126 Resolver::removeExtraRequire (const Capability & capability)
127 {
128     _extra_requires.erase (capability);
129 }
130
131 void
132 Resolver::addExtraConflict (const Capability & capability)
133 {
134     _extra_conflicts.insert (capability);
135 }
136
137 void
138 Resolver::removeExtraConflict (const Capability & capability)
139 {
140     _extra_conflicts.erase (capability);
141 }
142
143 void Resolver::removeQueueItem (const SolverQueueItem_Ptr item)
144 {
145     bool found = false;
146     for (SolverQueueItemList::const_iterator iter = _added_queue_items.begin();
147          iter != _added_queue_items.end(); iter++) {
148         if (*iter == item) {
149             _added_queue_items.remove(*iter);
150             found = true;
151             break;
152         }
153     }
154     if (!found) {
155         _removed_queue_items.push_back (item);
156         _removed_queue_items.unique ();
157     }
158 }
159 void Resolver::addQueueItem (const SolverQueueItem_Ptr item)
160 {
161     bool found = false;
162     for (SolverQueueItemList::const_iterator iter = _removed_queue_items.begin();
163          iter != _removed_queue_items.end(); iter++) {
164         if (*iter == item) {
165             _removed_queue_items.remove(*iter);
166             found = true;
167             break;
168         }
169     }
170     if (!found) {
171         _added_queue_items.push_back (item);
172         _added_queue_items.unique ();
173     }    
174 }
175
176 void
177 Resolver::addWeak (const PoolItem item)
178 {
179     _addWeak.push_back (item);
180 }
181
182 //---------------------------------------------------------------------------
183
184 struct UndoTransact : public resfilter::PoolItemFilterFunctor
185 {
186     ResStatus::TransactByValue resStatus;
187     UndoTransact ( const ResStatus::TransactByValue &status)
188         :resStatus(status)
189     { }
190
191     bool operator()( PoolItem item )            // only transacts() items go here
192     {
193         item.status().resetTransact( resStatus );// clear any solver/establish transactions
194         return true;
195     }
196 };
197
198
199 struct DoTransact : public resfilter::PoolItemFilterFunctor
200 {
201     ResStatus::TransactByValue resStatus;
202     DoTransact ( const ResStatus::TransactByValue &status)
203         :resStatus(status)
204     { }
205
206     bool operator()( PoolItem item )            // only transacts() items go here
207     {
208         item.status().setTransact( true, resStatus );
209         return true;
210     }
211 };
212
213
214 bool
215 Resolver::verifySystem ()
216 {
217     UndoTransact resetting (ResStatus::APPL_HIGH);
218
219     _DEBUG ("Resolver::verifySystem() ");
220     
221     _verifying = true;    
222
223     invokeOnEach ( _pool.begin(), _pool.end(),
224                    resfilter::ByTransact( ),                    // Resetting all transcations
225                    functor::functorRef<bool,PoolItem>(resetting) );
226
227     return resolvePool();
228 }
229
230
231 //----------------------------------------------------------------------------
232 // undo
233
234 void
235 Resolver::undo(void)
236 {
237     UndoTransact info(ResStatus::APPL_LOW);
238     MIL << "*** undo ***" << endl;
239     invokeOnEach ( _pool.begin(), _pool.end(),
240                    resfilter::ByTransact( ),                    // collect transacts from Pool to resolver queue
241                    functor::functorRef<bool,PoolItem>(info) );
242     //  Regard dependencies of the item weak onl
243     _addWeak.clear();
244
245     // Ignore Obsoletes
246     _noObsoletesCapability.clear();
247     _noObsoletesItem.clear();
248     _noObsoletesString.clear();   
249
250     // Additional QueueItems which has to be regarded by the solver
251     _removed_queue_items.clear();
252     _added_queue_items.clear();    
253
254     return;
255 }
256
257 void
258 Resolver::solverInit()
259 {
260     // Solving with the satsolver
261         static bool poolDumped = false;
262         MIL << "-------------- Calling SAT Solver -------------------" << endl;
263         if ( getenv("ZYPP_FULLLOG") ) {
264             Testcase testcase("/var/log/YaST2/autoTestcase");
265             if (!poolDumped) {
266                 testcase.createTestcase (*this, true, false); // dump pool
267                 poolDumped = true;
268             } else {
269                 testcase.createTestcase (*this, false, false); // write control file only
270             }
271         }
272
273         _satResolver->setFixsystem(false);
274         _satResolver->setIgnorealreadyrecommended(false);       
275         _satResolver->setAllowdowngrade(false);
276         _satResolver->setAllowarchchange(false);
277         _satResolver->setAllowvendorchange(false);
278         _satResolver->setAllowuninstall(false);
279         _satResolver->setUpdatesystem(false);
280         _satResolver->setAllowvirtualconflicts(false);
281         _satResolver->setNoupdateprovide(true);
282         _satResolver->setDosplitprovides(false);
283         
284         if (_upgradeMode) {
285             _satResolver->setAllowdowngrade(true);
286             _satResolver->setUpdatesystem(false); // not needed cause packages has already been evaluteted by distupgrade
287             _satResolver->setDosplitprovides(true);   
288         }
289
290         if (_forceResolve)
291             _satResolver->setAllowuninstall(true);
292         
293         if (_onlyRequires == indeterminate)
294             _satResolver->setOnlyRequires(ZConfig::instance().solver_onlyRequires());
295         else if (_onlyRequires)
296             _satResolver->setOnlyRequires(true);
297         else
298             _satResolver->setOnlyRequires(false);
299
300         if (_verifying)
301             _satResolver->setFixsystem(true);
302
303         if (_ignorealreadyrecommended)
304             _satResolver->setIgnorealreadyrecommended(true);    
305
306         // Resetting additional solver information
307         _isInstalledBy.clear();
308         _installs.clear();
309         _satifiedByInstalled.clear();
310         _installedSatisfied.clear();
311 }
312
313 bool
314 Resolver::resolvePool()
315 {
316     solverInit();
317     return _satResolver->resolvePool(_extra_requires, _extra_conflicts, _addWeak,
318                                      _noObsoletesCapability, _noObsoletesItem, _noObsoletesString   
319                                      );
320 }
321
322 bool
323 Resolver::resolveQueue(solver::detail::SolverQueueItemList & queue)
324 {
325     solverInit();
326     
327     // add/remove additional SolverQueueItems
328     for (SolverQueueItemList::const_iterator iter = _removed_queue_items.begin();
329          iter != _removed_queue_items.end(); iter++) {
330         for (SolverQueueItemList::const_iterator iterQueue = queue.begin(); iterQueue != queue.end(); iterQueue++) {
331             if ( (*iterQueue)->cmp(*iter) == 0) {           
332                 MIL << "remove from queue" << *iter;
333                 queue.remove(*iterQueue);
334                 break;
335             }       
336         }
337     }
338
339     for (SolverQueueItemList::const_iterator iter = _added_queue_items.begin();
340          iter != _added_queue_items.end(); iter++) {
341         bool found = false;
342         for (SolverQueueItemList::const_iterator iterQueue = queue.begin(); iterQueue != queue.end(); iterQueue++) {
343             if ( (*iterQueue)->cmp(*iter) == 0) {
344                 found = true;
345                 break;
346             }       
347         }
348         if (!found) {
349             MIL << "add to queue" << *iter;         
350             queue.push_back(*iter);
351         }
352     }
353
354     // The application has to take care to write these solutions back to e.g. selectables in order
355     // give the user a chance for changing these decisions again.
356     _removed_queue_items.clear();
357     _added_queue_items.clear();
358     
359     return _satResolver->resolveQueue(queue, _addWeak,
360                                       _noObsoletesCapability, _noObsoletesItem, _noObsoletesString);
361 }
362
363
364
365 ///////////////////////////////////////////////////////////////////
366 //
367 //
368 //      METHOD NAME : Resolver::checkUnmaintainedItems
369 //      METHOD TYPE : 
370 //
371 //      DESCRIPTION : Unmaintained packages which does not fit to 
372 //                    the updated system (broken dependencies) will be
373 //                    deleted.
374 //                    returns true if solving was successful
375 //
376 bool Resolver::checkUnmaintainedItems () {
377     int solverRuns = 1;
378     bool solverRet = resolvePool();
379     MIL << "Checking unmaintained items....." << endl;
380
381     while (!solverRet && solverRuns++ < MAXSOLVERRUNS) {
382         ResolverProblemList problemList = problems();
383         ProblemSolutionList solutionList;
384         PoolItemList problemItemList;   
385
386         for (ResolverProblemList::iterator iter = problemList.begin(); iter != problemList.end(); ++iter) {
387             ResolverProblem problem = **iter;
388             DBG << "Problem:" << endl;
389             DBG << problem.description() << endl;
390             DBG << problem.details() << endl;
391
392             ProblemSolutionList solutions = problem.solutions();
393             for (ProblemSolutionList::const_iterator iterSolution = solutions.begin();
394                  iterSolution != solutions.end(); ++iterSolution) {
395                 ProblemSolution_Ptr solution = *iterSolution;
396                 DBG << "   Solution:" << endl;
397                 DBG << "      " << solution->description() << endl;
398                 DBG << "      " << solution->details() << endl;         
399                 solver::detail::CSolutionActionList actionList = solution->actions();
400                 bool fitUnmaintained = false;
401                 PoolItemList deletedItems;
402                 for (CSolutionActionList::const_iterator iterActions = actionList.begin();
403                      iterActions != actionList.end(); ++iterActions) {
404                     TransactionSolutionAction_constPtr transactionAction = dynamic_pointer_cast<const TransactionSolutionAction>(*iterActions);
405                     if (transactionAction &&
406                         transactionAction->action() == REMOVE
407                         && _unmaintained_items.find(transactionAction->item()) != _unmaintained_items.end()) {
408                         // The solution contains unmaintained items ONLY which will be deleted. So take this solution
409                         fitUnmaintained = true;
410                         deletedItems.push_back (transactionAction->item());
411                     } else {
412                         fitUnmaintained = false;
413                     }
414                 }
415                 if (fitUnmaintained) {
416                     MIL << "Problem:" << endl;
417                     MIL << problem.description() << endl;
418                     MIL << problem.details() << endl;
419                     MIL << "Will be solved by removing unmaintained package(s)............" << endl;
420                     MIL << "   Solution:" << endl;
421                     MIL << "      " << solution->description() << endl;
422                     MIL << "      " << solution->details() << endl;                                 
423                     solutionList.push_back (solution);
424                     problemItemList.insert (problemItemList.end(), deletedItems.begin(), deletedItems.end() );
425                     break; // not regarding the other solutions
426                 }
427             }
428         }
429
430         if (!solutionList.empty()) {
431             applySolutions (solutionList);
432             // list of problematic items after doUpgrade() which is show to the user
433             _problem_items.insert (_problem_items.end(), problemItemList.begin(), problemItemList.end());
434             _problem_items.unique();
435         } else {
436             // break cause there is no other solution available by the next run
437             solverRuns = MAXSOLVERRUNS;
438         }
439         // next try
440         solverRet = resolvePool();      
441     }
442     return solverRet;
443 }
444
445
446 //----------------------------------------------------------------------------
447 // Getting more information about the solve results
448
449
450 void
451 Resolver::collectResolverInfo(void)
452 {
453     if ( _satResolver
454          && _isInstalledBy.empty()
455          && _installs.empty()) {
456
457         // generating new
458         PoolItemList itemsToInstall = _satResolver->resultItemsToInstall();
459
460         for (PoolItemList::const_iterator instIter = itemsToInstall.begin();
461              instIter != itemsToInstall.end(); instIter++) {
462             // Requires
463             for (Capabilities::const_iterator capIt = (*instIter)->dep (Dep::REQUIRES).begin(); capIt != (*instIter)->dep (Dep::REQUIRES).end(); ++capIt)
464             {
465                 sat::WhatProvides possibleProviders(*capIt);
466                 for_( iter, possibleProviders.begin(), possibleProviders.end() ) {
467                     PoolItem provider = ResPool::instance().find( *iter );
468                     
469                     // searching if this provider will already be installed
470                     bool found = false;
471                     bool alreadySetForInstallation = false;
472                     ItemCapKindMap::const_iterator pos = _isInstalledBy.find(provider);
473                     while (pos != _isInstalledBy.end()
474                            && pos->first == provider
475                            && !found) {
476                         alreadySetForInstallation = true;
477                         ItemCapKind capKind = pos->second;
478                         if (capKind.item == *instIter)  found = true;
479                         pos++;
480                     }
481
482                     if (!found
483                         && provider.status().isToBeInstalled()) {
484                         if (provider.status().isBySolver()) {
485                             ItemCapKind capKindisInstalledBy( *instIter, *capIt, Dep::REQUIRES, !alreadySetForInstallation );
486                             _isInstalledBy.insert (make_pair( provider, capKindisInstalledBy));
487                         } else {
488                             // no initial installation cause it has been set be e.g. user
489                             ItemCapKind capKindisInstalledBy( *instIter, *capIt, Dep::REQUIRES, false ); 
490                             _isInstalledBy.insert (make_pair( provider, capKindisInstalledBy));                     
491                         }
492                         ItemCapKind capKindisInstalledBy( provider, *capIt, Dep::REQUIRES, !alreadySetForInstallation );
493                         _installs.insert (make_pair( *instIter, capKindisInstalledBy));                 
494                     }
495
496                     if (provider.status().staysInstalled()) { // Is already satisfied by an item which is installed
497                         ItemCapKind capKindisInstalledBy( provider, *capIt, Dep::REQUIRES, false );
498                         _satifiedByInstalled.insert (make_pair( *instIter, capKindisInstalledBy));
499
500                         ItemCapKind installedSatisfied( *instIter, *capIt, Dep::REQUIRES, false ); 
501                         _installedSatisfied.insert (make_pair( provider, installedSatisfied));                      
502                     }
503                 }
504             }
505
506             if (!(_satResolver->onlyRequires())) {
507                 //Recommends
508                 for (Capabilities::const_iterator capIt = (*instIter)->dep (Dep::RECOMMENDS).begin(); capIt != (*instIter)->dep (Dep::RECOMMENDS).end(); ++capIt)
509                 {
510                     sat::WhatProvides possibleProviders(*capIt);
511                     for_( iter, possibleProviders.begin(), possibleProviders.end() ) {
512                         PoolItem provider = ResPool::instance().find( *iter );
513                     
514                         // searching if this provider will already be installed
515                         bool found = false;
516                         bool alreadySetForInstallation = false;
517                         ItemCapKindMap::const_iterator pos = _isInstalledBy.find(provider);
518                         while (pos != _isInstalledBy.end()
519                                && pos->first == provider
520                                && !found) {
521                             alreadySetForInstallation = true;
522                             ItemCapKind capKind = pos->second;
523                             if (capKind.item == *instIter)  found = true;
524                             pos++;
525                         }
526
527                         if (!found
528                             && provider.status().isToBeInstalled()) {
529                             if (provider.status().isBySolver()) {
530                                 ItemCapKind capKindisInstalledBy( *instIter, *capIt, Dep::RECOMMENDS, !alreadySetForInstallation );
531                                 _isInstalledBy.insert (make_pair( provider, capKindisInstalledBy));
532                             } else {
533                                 // no initial installation cause it has been set be e.g. user
534                                 ItemCapKind capKindisInstalledBy( *instIter, *capIt, Dep::RECOMMENDS, false ); 
535                                 _isInstalledBy.insert (make_pair( provider, capKindisInstalledBy));                         
536                             }
537                             ItemCapKind capKindisInstalledBy( provider, *capIt, Dep::RECOMMENDS, !alreadySetForInstallation );
538                             _installs.insert (make_pair( *instIter, capKindisInstalledBy));                     
539                         }
540
541                         if (provider.status().staysInstalled()) { // Is already satisfied by an item which is installed
542                             ItemCapKind capKindisInstalledBy( provider, *capIt, Dep::RECOMMENDS, false );
543                             _satifiedByInstalled.insert (make_pair( *instIter, capKindisInstalledBy));
544
545                             ItemCapKind installedSatisfied( *instIter, *capIt, Dep::RECOMMENDS, false ); 
546                             _installedSatisfied.insert (make_pair( provider, installedSatisfied));                          
547                         }                   
548                     }
549                 }
550
551                 //Supplements
552                 for (Capabilities::const_iterator capIt = (*instIter)->dep (Dep::SUPPLEMENTS).begin(); capIt != (*instIter)->dep (Dep::SUPPLEMENTS).end(); ++capIt)
553                 {
554                     sat::WhatProvides possibleProviders(*capIt);
555                     for_( iter, possibleProviders.begin(), possibleProviders.end() ) {
556                         PoolItem provider = ResPool::instance().find( *iter );
557                         // searching if this item will already be installed
558                         bool found = false;
559                         bool alreadySetForInstallation = false;
560                         ItemCapKindMap::const_iterator pos = _isInstalledBy.find(*instIter);
561                         while (pos != _isInstalledBy.end()
562                                && pos->first == *instIter
563                                && !found) {
564                             alreadySetForInstallation = true;
565                             ItemCapKind capKind = pos->second;
566                             if (capKind.item == provider)  found = true;
567                             pos++;
568                         }
569
570                         if (!found
571                             && instIter->status().isToBeInstalled()) {
572                             if (instIter->status().isBySolver()) {
573                                 ItemCapKind capKindisInstalledBy( provider, *capIt, Dep::SUPPLEMENTS, !alreadySetForInstallation );
574                                 _isInstalledBy.insert (make_pair( *instIter, capKindisInstalledBy));
575                             } else {
576                                 // no initial installation cause it has been set be e.g. user
577                                 ItemCapKind capKindisInstalledBy( provider, *capIt, Dep::SUPPLEMENTS, false ); 
578                                 _isInstalledBy.insert (make_pair( *instIter, capKindisInstalledBy));                        
579                             }
580                             ItemCapKind capKindisInstalledBy( *instIter, *capIt, Dep::SUPPLEMENTS, !alreadySetForInstallation );
581                             _installs.insert (make_pair( provider, capKindisInstalledBy));
582                         }
583
584                         if (instIter->status().staysInstalled()) { // Is already satisfied by an item which is installed
585                             ItemCapKind capKindisInstalledBy( *instIter, *capIt, Dep::SUPPLEMENTS, !alreadySetForInstallation );
586                             _satifiedByInstalled.insert (make_pair( provider, capKindisInstalledBy));
587
588                             ItemCapKind installedSatisfied( provider, *capIt, Dep::SUPPLEMENTS, false ); 
589                             _installedSatisfied.insert (make_pair( *instIter, installedSatisfied));                         
590                         }
591                     }
592                 }
593             }
594         }
595     }
596 }
597
598
599 const ItemCapKindList Resolver::isInstalledBy (const PoolItem item) {
600     ItemCapKindList ret;
601     collectResolverInfo();
602
603     for (ItemCapKindMap::const_iterator iter = _isInstalledBy.find(item); iter != _isInstalledBy.end();) {
604         ItemCapKind info = iter->second;
605         PoolItem iterItem = iter->first;
606         if (iterItem == item) {
607             ret.push_back(info);
608             iter++;
609         } else {
610             // exit
611             iter = _isInstalledBy.end();
612         }
613     }
614     return ret;
615 }
616
617 const ItemCapKindList Resolver::installs (const PoolItem item) {
618     ItemCapKindList ret;
619     collectResolverInfo();
620
621     for (ItemCapKindMap::const_iterator iter = _installs.find(item); iter != _installs.end();) {
622         ItemCapKind info = iter->second;
623         PoolItem iterItem = iter->first;
624         if (iterItem == item) {
625             ret.push_back(info);
626             iter++;
627         } else {
628             // exit
629             iter = _installs.end();
630         }
631     }
632     return ret;
633 }
634
635 const ItemCapKindList Resolver::satifiedByInstalled (const PoolItem item) {
636     ItemCapKindList ret;
637     collectResolverInfo();
638
639     for (ItemCapKindMap::const_iterator iter = _satifiedByInstalled.find(item); iter != _satifiedByInstalled.end();) {
640         ItemCapKind info = iter->second;
641         PoolItem iterItem = iter->first;
642         if (iterItem == item) {
643             ret.push_back(info);
644             iter++;
645         } else {
646             // exit
647             iter = _satifiedByInstalled.end();
648         }
649     }
650     return ret;
651 }
652
653 const ItemCapKindList Resolver::installedSatisfied (const PoolItem item) {
654     ItemCapKindList ret;
655     collectResolverInfo();
656
657     for (ItemCapKindMap::const_iterator iter = _installedSatisfied.find(item); iter != _installedSatisfied.end();) {
658         ItemCapKind info = iter->second;
659         PoolItem iterItem = iter->first;
660         if (iterItem == item) {
661             ret.push_back(info);
662             iter++;
663         } else {
664             // exit
665             iter = _installedSatisfied.end();
666         }
667     }
668     return ret;
669 }
670
671
672 ///////////////////////////////////////////////////////////////////
673     };// namespace detail
674     /////////////////////////////////////////////////////////////////////
675     /////////////////////////////////////////////////////////////////////
676   };// namespace solver
677   ///////////////////////////////////////////////////////////////////////
678   ///////////////////////////////////////////////////////////////////////
679 };// namespace zypp
680 /////////////////////////////////////////////////////////////////////////
681