summaryrefslogtreecommitdiffstats
path: root/meta/recipes-devtools/python/python-smartpm/smart-recommends.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta/recipes-devtools/python/python-smartpm/smart-recommends.patch')
-rw-r--r--meta/recipes-devtools/python/python-smartpm/smart-recommends.patch1362
1 files changed, 1362 insertions, 0 deletions
diff --git a/meta/recipes-devtools/python/python-smartpm/smart-recommends.patch b/meta/recipes-devtools/python/python-smartpm/smart-recommends.patch
new file mode 100644
index 0000000000..a41b1beaa9
--- /dev/null
+++ b/meta/recipes-devtools/python/python-smartpm/smart-recommends.patch
@@ -0,0 +1,1362 @@
1Handle recommended packages in core and rpm backends
2
3Identify and store recommended packages in the cache, add a query option
4to read them and ignore them if they are not present when installing.
5
6Initial identification code from Mark Hatle <mark.hatle@windriver.com>.
7
8Upstream-Status: Pending
9
10Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
11
12diff --git a/smart/backends/rpm/base.py b/smart/backends/rpm/base.py
13index 0489e11..b9e9cb2 100644
14--- a/smart/backends/rpm/base.py
15+++ b/smart/backends/rpm/base.py
16@@ -198,6 +198,29 @@ class RPMPackage(Package):
17 break
18 else:
19 return False
20+ srecs = fk(self.recommends)
21+ orecs = fk(other.recommends)
22+ if srecs != orecs:
23+ for srec in srecs:
24+ if srec.name[0] == "/" or srec in orecs:
25+ continue
26+ for orec in orecs:
27+ if (srec.name == orec.name and
28+ srec.relation == orec.relation and
29+ checkver(srec.version, orec.version)):
30+ break
31+ else:
32+ return False
33+ for orec in orecs:
34+ if orec.name[0] == "/" or orec in srecs:
35+ continue
36+ for srec in srecs:
37+ if (srec.name == orec.name and
38+ srec.relation == orec.relation and
39+ checkver(srec.version, orec.version)):
40+ break
41+ else:
42+ return False
43 return True
44
45 def coexists(self, other):
46diff --git a/smart/backends/rpm/header.py b/smart/backends/rpm/header.py
47index 31786cc..4880f43 100644
48--- a/smart/backends/rpm/header.py
49+++ b/smart/backends/rpm/header.py
50@@ -292,6 +292,7 @@ class RPMHeaderLoader(Loader):
51 f = [0]
52 elif type(f) != list:
53 f = [f]
54+ recdict = {}
55 reqdict = {}
56 for i in range(len(n)):
57 ni = n[i]
58@@ -308,10 +309,16 @@ class RPMHeaderLoader(Loader):
59 # RPMSENSE_SCRIPT_PREUN |
60 # RPMSENSE_SCRIPT_POST |
61 # RPMSENSE_SCRIPT_POSTUN == 7744
62- reqdict[(f[i]&7744 and PreReq or Req,
63- intern(ni), r, vi)] = True
64+ if (f[i]&rpm.RPMSENSE_MISSINGOK):
65+ recdict[(f[i]&7744 and PreReq or Req,
66+ intern(ni), r, vi)] = True
67+ else:
68+ reqdict[(f[i]&7744 and PreReq or Req,
69+ intern(ni), r, vi)] = True
70+ recargs = collapse_libc_requires(recdict.keys())
71 reqargs = collapse_libc_requires(reqdict.keys())
72 else:
73+ recargs = None
74 reqargs = None
75
76 n = h[1054] # RPMTAG_CONFLICTNAME
77@@ -365,7 +372,7 @@ class RPMHeaderLoader(Loader):
78 versionarch = "%s@%s" % (distversion, arch)
79
80 pkg = self.buildPackage((Pkg, name, versionarch),
81- prvargs, reqargs, upgargs, cnfargs)
82+ prvargs, reqargs, upgargs, cnfargs, recargs)
83 pkg.loaders[self] = offset
84 self._offsets[offset] = pkg
85 self._groups[pkg] = intern(h[rpm.RPMTAG_GROUP])
86@@ -583,8 +590,8 @@ class URPMILoader(RPMHeaderListLoader):
87 def setErrataFlags(self, flagdict):
88 self._flagdict = flagdict
89
90- def buildPackage(self, pkgargs, prvargs, reqargs, upgargs, cnfargs):
91- pkg = Loader.buildPackage(self, pkgargs, prvargs, reqargs, upgargs, cnfargs)
92+ def buildPackage(self, pkgargs, prvargs, reqargs, upgargs, cnfargs, recargs):
93+ pkg = Loader.buildPackage(self, pkgargs, prvargs, reqargs, upgargs, cnfargs, recargs)
94 name = pkgargs[1]
95 if hasattr(self, '_flagdict') and self._flagdict and name in self._flagdict:
96 if sysconf.getReadOnly():
97diff --git a/smart/backends/rpm/metadata.py b/smart/backends/rpm/metadata.py
98index 2c54f39..568fe06 100644
99--- a/smart/backends/rpm/metadata.py
100+++ b/smart/backends/rpm/metadata.py
101@@ -165,6 +165,7 @@ class RPMMetaDataLoader(Loader):
102 distepoch = None
103 info = {}
104 reqdict = {}
105+ recdict = {}
106 prvdict = {}
107 upgdict = {}
108 cnfdict = {}
109@@ -287,12 +288,16 @@ class RPMMetaDataLoader(Loader):
110
111 lasttag = queue[-1].tag
112 if lasttag == REQUIRES:
113- if elem.get("pre") == "1":
114- reqdict[(RPMPreRequires,
115- ename, erelation, eversion)] = True
116+ if elem.get("missingok") == "1":
117+ recdict[(RPMRequires,
118+ ename, erelation, eversion)] = True
119 else:
120- reqdict[(RPMRequires,
121- ename, erelation, eversion)] = True
122+ if elem.get("pre") == "1":
123+ reqdict[(RPMPreRequires,
124+ ename, erelation, eversion)] = True
125+ else:
126+ reqdict[(RPMRequires,
127+ ename, erelation, eversion)] = True
128
129 elif lasttag == PROVIDES:
130 if ename[0] == "/":
131@@ -328,6 +333,12 @@ class RPMMetaDataLoader(Loader):
132 (RPMProvides, x[1], x[3]) in prvdict or
133 system_provides.match(*x[:3]))]
134 reqargs = collapse_libc_requires(reqargs)
135+
136+ recargs = [x for x in recdict
137+ if not ((x[2] is None or "=" in x[2]) and
138+ (RPMProvides, x[1], x[3]) in prvdict or
139+ system_provides.match(*x[:3]))]
140+
141 prvargs = prvdict.keys()
142 cnfargs = cnfdict.keys()
143 upgargs = upgdict.keys()
144@@ -339,7 +350,7 @@ class RPMMetaDataLoader(Loader):
145 versionarch = "%s@%s" % (distversion, arch)
146
147 pkg = self.buildPackage((RPMPackage, name, versionarch),
148- prvargs, reqargs, upgargs, cnfargs)
149+ prvargs, reqargs, upgargs, cnfargs, recargs)
150 pkg.loaders[self] = info
151
152 # Store the provided files for future usage.
153@@ -362,6 +373,7 @@ class RPMMetaDataLoader(Loader):
154 distepoch = None
155 pkgid = None
156 reqdict.clear()
157+ recdict.clear()
158 prvdict.clear()
159 upgdict.clear()
160 cnfdict.clear()
161diff --git a/smart/cache.py b/smart/cache.py
162index b829825..cec8bb3 100644
163--- a/smart/cache.py
164+++ b/smart/cache.py
165@@ -32,7 +32,8 @@ class Package(object):
166 self.name = name
167 self.version = version
168 self.provides = ()
169- self.requires = ()
170+ self.requires = []
171+ self.recommends = []
172 self.upgrades = ()
173 self.conflicts = ()
174 self.installed = False
175@@ -55,7 +56,9 @@ class Package(object):
176 fk([x for x in self.provides if x.name[0] != "/"]) !=
177 fk([x for x in other.provides if x.name[0] != "/"]) or
178 fk([x for x in self.requires if x.name[0] != "/"]) !=
179- fk([x for x in other.requires if x.name[0] != "/"])):
180+ fk([x for x in other.requires if x.name[0] != "/"]) or
181+ fk([x for x in self.recommends if x.name[0] != "/"]) !=
182+ fk([x for x in other.recommends if x.name[0] != "/"])):
183 return False
184 return True
185
186@@ -110,6 +113,7 @@ class Package(object):
187 self.version,
188 self.provides,
189 self.requires,
190+ self.recommends,
191 self.upgrades,
192 self.conflicts,
193 self.installed,
194@@ -122,6 +126,7 @@ class Package(object):
195 self.version,
196 self.provides,
197 self.requires,
198+ self.recommends,
199 self.upgrades,
200 self.conflicts,
201 self.installed,
202@@ -274,6 +279,7 @@ class Provides(object):
203 self.version = version
204 self.packages = []
205 self.requiredby = ()
206+ self.recommendedby = ()
207 self.upgradedby = ()
208 self.conflictedby = ()
209
210@@ -401,7 +407,7 @@ class Loader(object):
211 def loadFileProvides(self, fndict):
212 pass
213
214- def buildPackage(self, pkgargs, prvargs, reqargs, upgargs, cnfargs):
215+ def buildPackage(self, pkgargs, prvargs, reqargs, upgargs, cnfargs, recargs = None):
216 cache = self._cache
217 pkg = pkgargs[0](*pkgargs[1:])
218 relpkgs = []
219@@ -427,6 +433,17 @@ class Loader(object):
220 relpkgs.append(req.packages)
221 pkg.requires.append(req)
222
223+ if recargs:
224+ pkg.recommends = []
225+ for args in recargs:
226+ rec = cache._objmap.get(args)
227+ if not rec:
228+ rec = args[0](*args[1:])
229+ cache._objmap[args] = rec
230+ cache._recommends.append(rec)
231+ relpkgs.append(rec.packages)
232+ pkg.recommends.append(rec)
233+
234 if upgargs:
235 pkg.upgrades = []
236 for args in upgargs:
237@@ -572,6 +589,7 @@ class Cache(object):
238 self._packages = []
239 self._provides = []
240 self._requires = []
241+ self._recommends = []
242 self._upgrades = []
243 self._conflicts = []
244 self._objmap = {}
245@@ -581,6 +599,8 @@ class Cache(object):
246 del prv.packages[:]
247 if prv.requiredby:
248 del prv.requiredby[:]
249+ if prv.recommendedby:
250+ del prv.recommendedby[:]
251 if prv.upgradedby:
252 del prv.upgradedby[:]
253 if prv.conflictedby:
254@@ -589,6 +609,10 @@ class Cache(object):
255 del req.packages[:]
256 if req.providedby:
257 del req.providedby[:]
258+ for rec in self._recommends:
259+ del rec.packages[:]
260+ if rec.providedby:
261+ del rec.providedby[:]
262 for upg in self._upgrades:
263 del upg.packages[:]
264 if upg.providedby:
265@@ -600,6 +624,7 @@ class Cache(object):
266 del self._packages[:]
267 del self._provides[:]
268 del self._requires[:]
269+ del self._recommends[:]
270 del self._upgrades[:]
271 del self._conflicts[:]
272 self._objmap.clear()
273@@ -621,6 +646,7 @@ class Cache(object):
274 packages = {}
275 provides = {}
276 requires = {}
277+ recommends = {}
278 upgrades = {}
279 conflicts = {}
280 objmap = self._objmap
281@@ -646,6 +672,11 @@ class Cache(object):
282 if req not in requires:
283 objmap[req.getInitArgs()] = req
284 requires[req] = True
285+ for rec in pkg.recommends[:]:
286+ rec.packages.append(pkg)
287+ if rec not in recommends:
288+ objmap[rec.getInitArgs()] = rec
289+ recommends[rec] = True
290 for upg in pkg.upgrades:
291 upg.packages.append(pkg)
292 if upg not in upgrades:
293@@ -659,6 +690,7 @@ class Cache(object):
294 self._packages[:] = packages.keys()
295 self._provides[:] = provides.keys()
296 self._requires[:] = requires.keys()
297+ self._recommends[:] = recommends.keys()
298 self._upgrades[:] = upgrades.keys()
299 self._conflicts[:] = conflicts.keys()
300
301@@ -710,6 +742,14 @@ class Cache(object):
302 lst.append(req)
303 else:
304 reqnames[name] = [req]
305+ recnames = {}
306+ for rec in self._recommends:
307+ for name in rec.getMatchNames():
308+ lst = recnames.get(name)
309+ if lst:
310+ lst.append(rec)
311+ else:
312+ recnames[name] = [rec]
313 upgnames = {}
314 for upg in self._upgrades:
315 for name in upg.getMatchNames():
316@@ -739,6 +779,18 @@ class Cache(object):
317 prv.requiredby.append(req)
318 else:
319 prv.requiredby = [req]
320+ lst = recnames.get(prv.name)
321+ if lst:
322+ for rec in lst:
323+ if rec.matches(prv):
324+ if rec.providedby:
325+ rec.providedby.append(prv)
326+ else:
327+ rec.providedby = [prv]
328+ if prv.recommendedby:
329+ prv.recommendedby.append(rec)
330+ else:
331+ prv.recommendedby = [rec]
332 lst = upgnames.get(prv.name)
333 if lst:
334 for upg in lst:
335@@ -782,6 +834,12 @@ class Cache(object):
336 else:
337 return [x for x in self._requires if x.name == name]
338
339+ def getRecommends(self, name=None):
340+ if not name:
341+ return self._recommends
342+ else:
343+ return [x for x in self._recommends if x.name == name]
344+
345 def getUpgrades(self, name=None):
346 if not name:
347 return self._upgrades
348@@ -807,6 +865,12 @@ class Cache(object):
349 for req in self._requires:
350 if prvname in req.getMatchNames() and req.matches(prv):
351 searcher.addResult(req)
352+ if searcher.recommends:
353+ for prv in searcher.recommends:
354+ prvname = prv.name
355+ for req in self._recommends:
356+ if prvname in req.getMatchNames() and req.matches(prv):
357+ searcher.addResult(req)
358 if searcher.upgrades:
359 for prv in searcher.upgrades:
360 prvname = prv.name
361@@ -839,6 +903,7 @@ class Cache(object):
362 self._packages = state["_packages"]
363 provides = {}
364 requires = {}
365+ recommends = {}
366 upgrades = {}
367 conflicts = {}
368 for pkg in self._packages:
369@@ -848,6 +913,9 @@ class Cache(object):
370 for req in pkg.requires:
371 req.packages.append(pkg)
372 requires[req] = True
373+ for rec in pkg.recommends:
374+ rec.packages.append(pkg)
375+ recommends[rec] = True
376 for upg in pkg.upgrades:
377 upg.packages.append(pkg)
378 upgrades[upg] = True
379@@ -856,6 +924,7 @@ class Cache(object):
380 conflicts[cnf] = True
381 self._provides = provides.keys()
382 self._requires = requires.keys()
383+ self._recommends = recommends.keys()
384 self._upgrades = upgrades.keys()
385 self._conflicts = conflicts.keys()
386 self._objmap = {}
387diff --git a/smart/ccache.c b/smart/ccache.c
388index 7541e26..7193185 100644
389--- a/smart/ccache.c
390+++ b/smart/ccache.c
391@@ -82,6 +82,7 @@ typedef struct {
392 PyObject *version;
393 PyObject *provides;
394 PyObject *requires;
395+ PyObject *recommends;
396 PyObject *upgrades;
397 PyObject *conflicts;
398 PyObject *installed;
399@@ -96,6 +97,7 @@ typedef struct {
400 PyObject *version;
401 PyObject *packages;
402 PyObject *requiredby;
403+ PyObject *recommendedby;
404 PyObject *upgradedby;
405 PyObject *conflictedby;
406 } ProvidesObject;
407@@ -123,6 +125,7 @@ typedef struct {
408 PyObject *_packages;
409 PyObject *_provides;
410 PyObject *_requires;
411+ PyObject *_recommends;
412 PyObject *_upgrades;
413 PyObject *_conflicts;
414 PyObject *_objmap;
415@@ -211,7 +214,8 @@ Package_init(PackageObject *self, PyObject *args)
416 Py_INCREF(self->name);
417 Py_INCREF(self->version);
418 self->provides = PyTuple_New(0);
419- self->requires = PyTuple_New(0);
420+ self->requires = PyList_New(0);
421+ self->recommends = PyList_New(0);
422 self->upgrades = PyTuple_New(0);
423 self->conflicts = PyTuple_New(0);
424 Py_INCREF(Py_False);
425@@ -228,6 +232,7 @@ Package_traverse(PackageObject *self, visitproc visit, void *arg)
426 {
427 Py_VISIT(self->provides);
428 Py_VISIT(self->requires);
429+ Py_VISIT(self->recommends);
430 Py_VISIT(self->upgrades);
431 Py_VISIT(self->conflicts);
432 Py_VISIT(self->loaders);
433@@ -239,6 +244,7 @@ Package_clear(PackageObject *self)
434 {
435 Py_CLEAR(self->provides);
436 Py_CLEAR(self->requires);
437+ Py_CLEAR(self->recommends);
438 Py_CLEAR(self->upgrades);
439 Py_CLEAR(self->conflicts);
440 Py_CLEAR(self->loaders);
441@@ -252,6 +258,7 @@ Package_dealloc(PackageObject *self)
442 Py_XDECREF(self->version);
443 Py_XDECREF(self->provides);
444 Py_XDECREF(self->requires);
445+ Py_XDECREF(self->recommends);
446 Py_XDECREF(self->upgrades);
447 Py_XDECREF(self->conflicts);
448 Py_XDECREF(self->installed);
449@@ -453,6 +460,46 @@ Package_equals(PackageObject *self, PackageObject *other)
450 }
451 }
452
453+ ilen = 0;
454+ jlen = 0;
455+ for (i = 0; i != PyList_GET_SIZE(self->recommends); i++) {
456+ PyObject *item = PyList_GET_ITEM(self->recommends, i);
457+ if (!PyObject_IsInstance(item, (PyObject *)&Depends_Type)) {
458+ PyErr_SetString(PyExc_TypeError, "Depends instance expected");
459+ return NULL;
460+ }
461+ if (STR(((DependsObject *)item)->name)[0] != '/')
462+ ilen += 1;
463+ }
464+ for (j = 0; j != PyList_GET_SIZE(other->recommends); j++) {
465+ PyObject *item = PyList_GET_ITEM(other->recommends, j);
466+ if (!PyObject_IsInstance(item, (PyObject *)&Depends_Type)) {
467+ PyErr_SetString(PyExc_TypeError, "Depends instance expected");
468+ return NULL;
469+ }
470+ if (STR(((DependsObject *)item)->name)[0] != '/')
471+ jlen += 1;
472+ }
473+ if (ilen != jlen) {
474+ ret = Py_False;
475+ goto exit;
476+ }
477+
478+ ilen = PyList_GET_SIZE(self->recommends);
479+ jlen = PyList_GET_SIZE(other->recommends);
480+ for (i = 0; i != ilen; i++) {
481+ PyObject *item = PyList_GET_ITEM(self->recommends, i);
482+ if (STR(((DependsObject *)item)->name)[0] != '/') {
483+ for (j = 0; j != jlen; j++)
484+ if (item == PyList_GET_ITEM(other->recommends, j))
485+ break;
486+ if (j == jlen) {
487+ ret = Py_False;
488+ goto exit;
489+ }
490+ }
491+ }
492+
493 exit:
494 Py_INCREF(ret);
495 return ret;
496@@ -606,13 +653,14 @@ Package_getPriority(PackageObject *self, PyObject *args)
497 static PyObject *
498 Package__getstate__(PackageObject *self, PyObject *args)
499 {
500- PyObject *state = PyTuple_New(10);
501+ PyObject *state = PyTuple_New(11);
502 if (!state) return NULL;
503
504 Py_INCREF(self->name);
505 Py_INCREF(self->version);
506 Py_INCREF(self->provides);
507 Py_INCREF(self->requires);
508+ Py_INCREF(self->recommends);
509 Py_INCREF(self->upgrades);
510 Py_INCREF(self->conflicts);
511 Py_INCREF(self->installed);
512@@ -620,16 +668,17 @@ Package__getstate__(PackageObject *self, PyObject *args)
513 Py_INCREF(self->priority);
514 Py_INCREF(self->loaders);
515
516- PyTuple_SET_ITEM(state, 0, self->name);
517- PyTuple_SET_ITEM(state, 1, self->version);
518- PyTuple_SET_ITEM(state, 2, self->provides);
519- PyTuple_SET_ITEM(state, 3, self->requires);
520- PyTuple_SET_ITEM(state, 4, self->upgrades);
521- PyTuple_SET_ITEM(state, 5, self->conflicts);
522- PyTuple_SET_ITEM(state, 6, self->installed);
523- PyTuple_SET_ITEM(state, 7, self->essential);
524- PyTuple_SET_ITEM(state, 8, self->priority);
525- PyTuple_SET_ITEM(state, 9, self->loaders);
526+ PyTuple_SET_ITEM(state, 0, self->name);
527+ PyTuple_SET_ITEM(state, 1, self->version);
528+ PyTuple_SET_ITEM(state, 2, self->provides);
529+ PyTuple_SET_ITEM(state, 3, self->requires);
530+ PyTuple_SET_ITEM(state, 4, self->recommends);
531+ PyTuple_SET_ITEM(state, 5, self->upgrades);
532+ PyTuple_SET_ITEM(state, 6, self->conflicts);
533+ PyTuple_SET_ITEM(state, 7, self->installed);
534+ PyTuple_SET_ITEM(state, 8, self->essential);
535+ PyTuple_SET_ITEM(state, 9, self->priority);
536+ PyTuple_SET_ITEM(state, 10, self->loaders);
537
538 return state;
539 }
540@@ -637,7 +686,7 @@ Package__getstate__(PackageObject *self, PyObject *args)
541 static PyObject *
542 Package__setstate__(PackageObject *self, PyObject *state)
543 {
544- if (!PyTuple_Check(state) || PyTuple_GET_SIZE(state) != 10) {
545+ if (!PyTuple_Check(state) || PyTuple_GET_SIZE(state) != 11) {
546 PyErr_SetString(StateVersionError, "");
547 return NULL;
548 }
549@@ -645,18 +694,20 @@ Package__setstate__(PackageObject *self, PyObject *state)
550 self->version = PyTuple_GET_ITEM(state, 1);
551 self->provides = PyTuple_GET_ITEM(state, 2);
552 self->requires = PyTuple_GET_ITEM(state, 3);
553- self->upgrades = PyTuple_GET_ITEM(state, 4);
554- self->conflicts = PyTuple_GET_ITEM(state, 5);
555- self->installed = PyTuple_GET_ITEM(state, 6);
556- self->essential = PyTuple_GET_ITEM(state, 7);
557- self->priority = PyTuple_GET_ITEM(state, 8);
558- self->loaders = PyTuple_GET_ITEM(state, 9);
559+ self->recommends = PyTuple_GET_ITEM(state, 4);
560+ self->upgrades = PyTuple_GET_ITEM(state, 5);
561+ self->conflicts = PyTuple_GET_ITEM(state, 6);
562+ self->installed = PyTuple_GET_ITEM(state, 7);
563+ self->essential = PyTuple_GET_ITEM(state, 8);
564+ self->priority = PyTuple_GET_ITEM(state, 9);
565+ self->loaders = PyTuple_GET_ITEM(state, 10);
566
567
568 Py_INCREF(self->name);
569 Py_INCREF(self->version);
570 Py_INCREF(self->provides);
571 Py_INCREF(self->requires);
572+ Py_INCREF(self->recommends);
573 Py_INCREF(self->upgrades);
574 Py_INCREF(self->conflicts);
575 Py_INCREF(self->installed);
576@@ -686,6 +737,7 @@ static PyMemberDef Package_members[] = {
577 {"version", T_OBJECT, OFF(version), 0, 0},
578 {"provides", T_OBJECT, OFF(provides), 0, 0},
579 {"requires", T_OBJECT, OFF(requires), 0, 0},
580+ {"recommends", T_OBJECT, OFF(recommends), 0, 0},
581 {"upgrades", T_OBJECT, OFF(upgrades), 0, 0},
582 {"conflicts", T_OBJECT, OFF(conflicts), 0, 0},
583 {"installed", T_OBJECT, OFF(installed), 0, 0},
584@@ -750,6 +802,7 @@ Provides_init(ProvidesObject *self, PyObject *args)
585 Py_INCREF(self->version);
586 self->packages = PyList_New(0);
587 self->requiredby = PyTuple_New(0);
588+ self->recommendedby = PyTuple_New(0);
589 self->upgradedby = PyTuple_New(0);
590 self->conflictedby = PyTuple_New(0);
591 return 0;
592@@ -760,6 +813,7 @@ Provides_traverse(ProvidesObject *self, visitproc visit, void *arg)
593 {
594 Py_VISIT(self->packages);
595 Py_VISIT(self->requiredby);
596+ Py_VISIT(self->recommendedby);
597 Py_VISIT(self->upgradedby);
598 Py_VISIT(self->conflictedby);
599 return 0;
600@@ -770,6 +824,7 @@ Provides_clear(ProvidesObject *self)
601 {
602 Py_CLEAR(self->packages);
603 Py_CLEAR(self->requiredby);
604+ Py_CLEAR(self->recommendedby);
605 Py_CLEAR(self->upgradedby);
606 Py_CLEAR(self->conflictedby);
607 return 0;
608@@ -782,6 +837,7 @@ Provides_dealloc(ProvidesObject *self)
609 Py_XDECREF(self->version);
610 Py_XDECREF(self->packages);
611 Py_XDECREF(self->requiredby);
612+ Py_XDECREF(self->recommendedby);
613 Py_XDECREF(self->upgradedby);
614 Py_XDECREF(self->conflictedby);
615 self->ob_type->tp_free((PyObject *)self);
616@@ -960,6 +1016,7 @@ static PyMemberDef Provides_members[] = {
617 {"version", T_OBJECT, OFF(version), 0, 0},
618 {"packages", T_OBJECT, OFF(packages), 0, 0},
619 {"requiredby", T_OBJECT, OFF(requiredby), 0, 0},
620+ {"recommendedby", T_OBJECT, OFF(recommendedby), 0, 0},
621 {"upgradedby", T_OBJECT, OFF(upgradedby), 0, 0},
622 {"conflictedby", T_OBJECT, OFF(conflictedby), 0, 0},
623 {NULL}
624@@ -1555,6 +1612,7 @@ Loader_buildPackage(LoaderObject *self, PyObject *args)
625 PyObject *reqargs;
626 PyObject *upgargs;
627 PyObject *cnfargs;
628+ PyObject *recargs = NULL;
629 PyObject *callargs;
630
631 PyObject *pkg;
632@@ -1574,9 +1632,10 @@ Loader_buildPackage(LoaderObject *self, PyObject *args)
633
634 cache = (CacheObject *)self->_cache;
635
636- if (!PyArg_ParseTuple(args, "O!O&O&O&O&", &PyTuple_Type, &pkgargs,
637+ if (!PyArg_ParseTuple(args, "O!O&O&O&O&|O&", &PyTuple_Type, &pkgargs,
638 mylist, &prvargs, mylist, &reqargs,
639- mylist, &upgargs, mylist, &cnfargs))
640+ mylist, &upgargs, mylist, &cnfargs,
641+ mylist, &recargs))
642 return NULL;
643
644 if (PyTuple_GET_SIZE(pkgargs) < 2) {
645@@ -1701,6 +1760,59 @@ Loader_buildPackage(LoaderObject *self, PyObject *args)
646 }
647 }
648
649+ /* if recargs: */
650+ if (recargs) {
651+ int i = 0;
652+ int len = PyList_GET_SIZE(recargs);
653+ /* pkg.recommends = [] */
654+ Py_DECREF(pkgobj->recommends);
655+ pkgobj->recommends = PyList_New(len);
656+ /* for args in recargs: */
657+ for (; i != len; i++) {
658+ PyObject *args = PyList_GET_ITEM(recargs, i);
659+ DependsObject *recobj;
660+ PyObject *rec;
661+
662+ if (!PyTuple_Check(args)) {
663+ PyErr_SetString(PyExc_TypeError,
664+ "Item in recargs is not a tuple");
665+ return NULL;
666+ }
667+
668+ /* rec = cache._objmap.get(args) */
669+ rec = PyDict_GetItem(cache->_objmap, args);
670+ recobj = (DependsObject *)rec;
671+
672+ /* if not rec: */
673+ if (!rec) {
674+ if (!PyTuple_Check(args) || PyTuple_GET_SIZE(args) < 2) {
675+ PyErr_SetString(PyExc_ValueError, "Invalid recargs tuple");
676+ return NULL;
677+ }
678+ /* rec = args[0](*args[1:]) */
679+ callargs = PyTuple_GetSlice(args, 1, PyTuple_GET_SIZE(args));
680+ rec = PyObject_CallObject(PyTuple_GET_ITEM(args, 0), callargs);
681+ Py_DECREF(callargs);
682+ if (!rec) return NULL;
683+ recobj = (DependsObject *)rec;
684+
685+ /* cache._objmap[args] = rec */
686+ PyDict_SetItem(cache->_objmap, args, rec);
687+ Py_DECREF(rec);
688+
689+ /* cache._recommends.append(rec) */
690+ PyList_Append(cache->_recommends, rec);
691+ }
692+
693+ /* relpkgs.append(rec.packages) */
694+ PyList_Append(relpkgs, recobj->packages);
695+
696+ /* pkg.recommends.append(rec) */
697+ Py_INCREF(rec);
698+ PyList_SET_ITEM(pkgobj->recommends, i, rec);
699+ }
700+ }
701+
702 /* if upgargs: */
703 if (upgargs) {
704 int i = 0;
705@@ -2391,6 +2503,7 @@ Cache_init(CacheObject *self, PyObject *args)
706 self->_packages = PyList_New(0);
707 self->_provides = PyList_New(0);
708 self->_requires = PyList_New(0);
709+ self->_recommends = PyList_New(0);
710 self->_upgrades = PyList_New(0);
711 self->_conflicts = PyList_New(0);
712 self->_objmap = PyDict_New();
713@@ -2404,6 +2517,7 @@ Cache_traverse(CacheObject *self, visitproc visit, void *arg)
714 Py_VISIT(self->_packages);
715 Py_VISIT(self->_provides);
716 Py_VISIT(self->_requires);
717+ Py_VISIT(self->_recommends);
718 Py_VISIT(self->_upgrades);
719 Py_VISIT(self->_conflicts);
720 Py_VISIT(self->_objmap);
721@@ -2417,6 +2531,7 @@ Cache_clear(CacheObject *self)
722 Py_CLEAR(self->_packages);
723 Py_CLEAR(self->_provides);
724 Py_CLEAR(self->_requires);
725+ Py_CLEAR(self->_recommends);
726 Py_CLEAR(self->_upgrades);
727 Py_CLEAR(self->_conflicts);
728 Py_CLEAR(self->_objmap);
729@@ -2430,6 +2545,7 @@ Cache_dealloc(CacheObject *self)
730 Py_XDECREF(self->_packages);
731 Py_XDECREF(self->_provides);
732 Py_XDECREF(self->_requires);
733+ Py_XDECREF(self->_recommends);
734 Py_XDECREF(self->_upgrades);
735 Py_XDECREF(self->_conflicts);
736 Py_XDECREF(self->_objmap);
737@@ -2449,6 +2565,8 @@ Cache_reset(CacheObject *self, PyObject *args)
738 LIST_CLEAR(prvobj->packages);
739 if (PyList_Check(prvobj->requiredby))
740 LIST_CLEAR(prvobj->requiredby);
741+ if (PyList_Check(prvobj->recommendedby))
742+ LIST_CLEAR(prvobj->recommendedby);
743 if (PyList_Check(prvobj->upgradedby))
744 LIST_CLEAR(prvobj->upgradedby);
745 if (PyList_Check(prvobj->conflictedby))
746@@ -2464,6 +2582,16 @@ Cache_reset(CacheObject *self, PyObject *args)
747 if (PyList_Check(reqobj->providedby))
748 LIST_CLEAR(reqobj->providedby);
749 }
750+ len = PyList_GET_SIZE(self->_recommends);
751+ for (i = 0; i != len; i++) {
752+ DependsObject *reqobj;
753+ PyObject *req;
754+ req = PyList_GET_ITEM(self->_recommends, i);
755+ reqobj = (DependsObject *)req;
756+ LIST_CLEAR(reqobj->packages);
757+ if (PyList_Check(reqobj->providedby))
758+ LIST_CLEAR(reqobj->providedby);
759+ }
760 len = PyList_GET_SIZE(self->_upgrades);
761 for (i = 0; i != len; i++) {
762 DependsObject *upgobj;
763@@ -2487,6 +2615,7 @@ Cache_reset(CacheObject *self, PyObject *args)
764 LIST_CLEAR(self->_packages);
765 LIST_CLEAR(self->_provides);
766 LIST_CLEAR(self->_requires);
767+ LIST_CLEAR(self->_recommends);
768 LIST_CLEAR(self->_upgrades);
769 LIST_CLEAR(self->_conflicts);
770 PyDict_Clear(self->_objmap);
771@@ -2534,6 +2663,7 @@ Cache__reload(CacheObject *self, PyObject *args)
772 packages = {}
773 provides = {}
774 requires = {}
775+ recommends = {}
776 upgrades = {}
777 conflicts = {}
778 objmap = self._objmap
779@@ -2541,11 +2671,12 @@ Cache__reload(CacheObject *self, PyObject *args)
780 PyObject *packages = PyDict_New();
781 PyObject *provides = PyDict_New();
782 PyObject *requires = PyDict_New();
783+ PyObject *recommends = PyDict_New();
784 PyObject *upgrades = PyDict_New();
785 PyObject *conflicts = PyDict_New();
786 PyObject *objmap = self->_objmap;
787 int i, ilen;
788- if (!packages || !provides || !requires || !conflicts)
789+ if (!packages || !provides || !requires || !recommends || !conflicts )
790 return NULL;
791
792 /* for loader in loaders: */
793@@ -2679,6 +2810,30 @@ Cache__reload(CacheObject *self, PyObject *args)
794 }
795
796 /*
797+ for rec in pkg.recommends:
798+ rec.packages.append(pkg)
799+ if rec not in recommends:
800+ recommends[rec] = True
801+ objmap[rec.getInitArgs()] = rec
802+ */
803+ if (PyList_Check(pkg->recommends)) {
804+ klen = PyList_GET_SIZE(pkg->recommends);
805+ for (k = 0; k != klen; k++) {
806+ PyObject *rec = PyList_GET_ITEM(pkg->recommends, k);
807+ PyList_Append(((DependsObject *)rec)->packages,
808+ (PyObject *)pkg);
809+ if (!PyDict_GetItem(recommends, rec)) {
810+ PyDict_SetItem(recommends, rec, Py_True);
811+ args = PyObject_CallMethod(rec, "getInitArgs",
812+ NULL);
813+ if (!args) return NULL;
814+ PyDict_SetItem(objmap, args, rec);
815+ Py_DECREF(args);
816+ }
817+ }
818+ }
819+
820+ /*
821 for upg in pkg.upgrades:
822 upg.packages.append(pkg)
823 if upg not in upgrades:
824@@ -2747,6 +2902,11 @@ Cache__reload(CacheObject *self, PyObject *args)
825 self->_requires = PyDict_Keys(requires);
826 Py_DECREF(requires);
827
828+ /* self._recommends[:] = recommends.keys() */
829+ Py_DECREF(self->_recommends);
830+ self->_recommends = PyDict_Keys(recommends);
831+ Py_DECREF(recommends);
832+
833 /* self._upgrades[:] = upgrades.keys() */
834 Py_DECREF(self->_upgrades);
835 self->_upgrades = PyDict_Keys(upgrades);
836@@ -2852,7 +3012,7 @@ PyObject *
837 Cache_linkDeps(CacheObject *self, PyObject *args)
838 {
839 int i, j, len;
840- PyObject *reqnames, *upgnames, *cnfnames;
841+ PyObject *reqnames, *recnames, *upgnames, *cnfnames;
842 PyObject *lst;
843
844 /* reqnames = {} */
845@@ -2896,6 +3056,47 @@ Cache_linkDeps(CacheObject *self, PyObject *args)
846 Py_DECREF(seq);
847 }
848
849+ /* recnames = {} */
850+ recnames = PyDict_New();
851+ /* for rec in self._recommends: */
852+ len = PyList_GET_SIZE(self->_recommends);
853+ for (i = 0; i != len; i++) {
854+ PyObject *rec = PyList_GET_ITEM(self->_recommends, i);
855+
856+ /* for name in rec.getMatchNames(): */
857+ PyObject *names = PyObject_CallMethod(rec, "getMatchNames", NULL);
858+ PyObject *seq = PySequence_Fast(names, "getMatchNames() returned "
859+ "non-sequence object");
860+ int nameslen;
861+ if (!seq) return NULL;
862+ nameslen = PySequence_Fast_GET_SIZE(seq);
863+ for (j = 0; j != nameslen; j++) {
864+ PyObject *name = PySequence_Fast_GET_ITEM(seq, j);
865+
866+ /* lst = recnames.get(name) */
867+ lst = PyDict_GetItem(recnames, name);
868+
869+ /*
870+ if lst:
871+ lst.append(rec)
872+ else:
873+ recnames[name] = [rec]
874+ */
875+ if (lst) {
876+ PyList_Append(lst, rec);
877+ } else {
878+ lst = PyList_New(1);
879+ Py_INCREF(rec);
880+ PyList_SET_ITEM(lst, 0, rec);
881+ PyDict_SetItem(recnames, name, lst);
882+ Py_DECREF(lst);
883+ }
884+ }
885+
886+ Py_DECREF(names);
887+ Py_DECREF(seq);
888+ }
889+
890 /* upgnames = {} */
891 upgnames = PyDict_New();
892 /* for upg in self._upgrades: */
893@@ -3035,6 +3236,56 @@ Cache_linkDeps(CacheObject *self, PyObject *args)
894 }
895 }
896
897+ /* lst = recnames.get(prv.name) */
898+ lst = PyDict_GetItem(recnames, prv->name);
899+
900+ /* if lst: */
901+ if (lst) {
902+ /* for rec in lst: */
903+ int reclen = PyList_GET_SIZE(lst);
904+ for (j = 0; j != reclen; j++) {
905+ DependsObject *rec = (DependsObject *)PyList_GET_ITEM(lst, j);
906+ /* if rec.matches(prv): */
907+ PyObject *ret = PyObject_CallMethod((PyObject *)rec, "matches",
908+ "O", (PyObject *)prv);
909+ if (!ret) return NULL;
910+ if (PyObject_IsTrue(ret)) {
911+ /*
912+ if rec.providedby:
913+ rec.providedby.append(prv)
914+ else:
915+ rec.providedby = [prv]
916+ */
917+ if (PyList_Check(rec->providedby)) {
918+ PyList_Append(rec->providedby, (PyObject *)prv);
919+ } else {
920+ PyObject *_lst = PyList_New(1);
921+ Py_INCREF(prv);
922+ PyList_SET_ITEM(_lst, 0, (PyObject *)prv);
923+ Py_DECREF(rec->providedby);
924+ rec->providedby = _lst;
925+ }
926+
927+ /*
928+ if prv.recommendedby:
929+ prv.recommendedby.append(prv)
930+ else:
931+ prv.recommendedby = [prv]
932+ */
933+ if (PyList_Check(prv->recommendedby)) {
934+ PyList_Append(prv->recommendedby, (PyObject *)rec);
935+ } else {
936+ PyObject *_lst = PyList_New(1);
937+ Py_INCREF(rec);
938+ PyList_SET_ITEM(_lst, 0, (PyObject *)rec);
939+ Py_DECREF(prv->recommendedby);
940+ prv->recommendedby = _lst;
941+ }
942+ }
943+ Py_DECREF(ret);
944+ }
945+ }
946+
947 /* lst = upgnames.get(prv.name) */
948 lst = PyDict_GetItem(upgnames, prv->name);
949
950@@ -3139,6 +3390,7 @@ Cache_linkDeps(CacheObject *self, PyObject *args)
951 }
952
953 Py_DECREF(reqnames);
954+ Py_DECREF(recnames);
955 Py_DECREF(upgnames);
956 Py_DECREF(cnfnames);
957
958@@ -3215,6 +3467,29 @@ Cache_getRequires(CacheObject *self, PyObject *args)
959 }
960
961 PyObject *
962+Cache_getRecommends(CacheObject *self, PyObject *args)
963+{
964+ const char *name = NULL;
965+ PyObject *lst;
966+ int i, len;
967+ if (!PyArg_ParseTuple(args, "|s", &name))
968+ return NULL;
969+ if (!name) {
970+ Py_INCREF(self->_recommends);
971+ return self->_recommends;
972+ }
973+ lst = PyList_New(0);
974+ len = PyList_GET_SIZE(self->_recommends);
975+ for (i = 0; i != len; i++) {
976+ DependsObject *rec =
977+ (DependsObject*)PyList_GET_ITEM(self->_recommends, i);
978+ if (strcmp(STR(rec->name), name) == 0)
979+ PyList_Append(lst, (PyObject *)rec);
980+ }
981+ return lst;
982+}
983+
984+PyObject *
985 Cache_getUpgrades(CacheObject *self, PyObject *args)
986 {
987 const char *name = NULL;
988@@ -3324,6 +3599,38 @@ Cache_search(CacheObject *self, PyObject *searcher)
989 }
990 Py_DECREF(lst);
991
992+ lst = PyObject_GetAttrString(searcher, "recommends");
993+ if (lst == NULL || !PyList_Check(lst)) {
994+ PyErr_SetString(PyExc_TypeError, "Invalid recommends attribute");
995+ return NULL;
996+ }
997+ for (i = 0; i != PyList_GET_SIZE(lst); i++) {
998+ ProvidesObject *prv = (ProvidesObject *)PyList_GET_ITEM(lst, i);
999+ for (j = 0; j != PyList_GET_SIZE(self->_recommends); j++) {
1000+ PyObject *rec = PyList_GET_ITEM(self->_recommends, j);
1001+ PyObject *names = PyObject_CallMethod(rec, "getMatchNames", NULL);
1002+ PyObject *seq = PySequence_Fast(names, "getMatchNames() returned "
1003+ "non-sequence object");
1004+ if (seq == NULL) return NULL;
1005+ for (k = 0; k != PySequence_Fast_GET_SIZE(seq); k++) {
1006+ if (strcmp(PyString_AS_STRING(PySequence_Fast_GET_ITEM(seq, k)),
1007+ PyString_AS_STRING(prv->name)) == 0) {
1008+ res = PyObject_CallMethod(rec, "matches", "O", prv);
1009+ if (res == NULL)
1010+ return NULL;
1011+ if (PyObject_IsTrue(res))
1012+ CALLMETHOD(searcher, "addResult", "O", rec);
1013+ Py_DECREF(res);
1014+ break;
1015+ }
1016+ }
1017+
1018+ Py_DECREF(names);
1019+ Py_DECREF(seq);
1020+ }
1021+ }
1022+ Py_DECREF(lst);
1023+
1024 lst = PyObject_GetAttrString(searcher, "upgrades");
1025 if (lst == NULL || !PyList_Check(lst)) {
1026 PyErr_SetString(PyExc_TypeError, "Invalid upgrades attribute");
1027@@ -3420,7 +3727,7 @@ Cache__getstate__(CacheObject *self, PyObject *args)
1028 static PyObject *
1029 Cache__setstate__(CacheObject *self, PyObject *state)
1030 {
1031- PyObject *provides, *requires, *upgrades, *conflicts;
1032+ PyObject *provides, *requires, *recommends, *upgrades, *conflicts;
1033 int i, ilen;
1034 int j, jlen;
1035
1036@@ -3452,11 +3759,13 @@ Cache__setstate__(CacheObject *self, PyObject *state)
1037 /*
1038 provides = {}
1039 requires = {}
1040+ recommends = {}
1041 upgrades = {}
1042 conflicts = {}
1043 */
1044 provides = PyDict_New();
1045 requires = PyDict_New();
1046+ recommends = PyDict_New();
1047 upgrades = PyDict_New();
1048 conflicts = PyDict_New();
1049
1050@@ -3497,6 +3806,21 @@ Cache__setstate__(CacheObject *self, PyObject *state)
1051 }
1052
1053 /*
1054+ for rec in pkg.recommends:
1055+ rec.packages.append(pkg)
1056+ recommends[rec] = True
1057+ */
1058+ if (PyList_Check(pkgobj->recommends)) {
1059+ jlen = PyList_GET_SIZE(pkgobj->recommends);
1060+ for (j = 0; j != jlen; j++) {
1061+ PyObject *rec = PyList_GET_ITEM(pkgobj->recommends, j);
1062+ DependsObject *recobj = (DependsObject *)rec;
1063+ PyList_Append(recobj->packages, pkg);
1064+ PyDict_SetItem(recommends, rec, Py_True);
1065+ }
1066+ }
1067+
1068+ /*
1069 for upg in pkg.upgrades:
1070 upg.packages.append(pkg)
1071 upgrades[upg] = True
1072@@ -3525,6 +3849,7 @@ Cache__setstate__(CacheObject *self, PyObject *state)
1073 PyDict_SetItem(conflicts, cnf, Py_True);
1074 }
1075 }
1076+
1077 }
1078
1079 /* self._provides = provides.keys() */
1080@@ -3535,6 +3860,10 @@ Cache__setstate__(CacheObject *self, PyObject *state)
1081 self->_requires = PyDict_Keys(requires);
1082 Py_DECREF(requires);
1083
1084+ /* self._recommends = recommends.keys() */
1085+ self->_recommends = PyDict_Keys(recommends);
1086+ Py_DECREF(recommends);
1087+
1088 /* self._upgrades = upgrades.keys() */
1089 self->_upgrades = PyDict_Keys(upgrades);
1090 Py_DECREF(upgrades);
1091@@ -3562,6 +3891,7 @@ static PyMethodDef Cache_methods[] = {
1092 {"getPackages", (PyCFunction)Cache_getPackages, METH_VARARGS, NULL},
1093 {"getProvides", (PyCFunction)Cache_getProvides, METH_VARARGS, NULL},
1094 {"getRequires", (PyCFunction)Cache_getRequires, METH_VARARGS, NULL},
1095+ {"getRecommends", (PyCFunction)Cache_getRecommends, METH_VARARGS, NULL},
1096 {"getUpgrades", (PyCFunction)Cache_getUpgrades, METH_VARARGS, NULL},
1097 {"getConflicts", (PyCFunction)Cache_getConflicts, METH_VARARGS, NULL},
1098 {"search", (PyCFunction)Cache_search, METH_O, NULL},
1099@@ -3576,6 +3906,7 @@ static PyMemberDef Cache_members[] = {
1100 {"_packages", T_OBJECT, OFF(_packages), RO, 0},
1101 {"_provides", T_OBJECT, OFF(_provides), RO, 0},
1102 {"_requires", T_OBJECT, OFF(_requires), RO, 0},
1103+ {"_recommends", T_OBJECT, OFF(_recommends), RO, 0},
1104 {"_upgrades", T_OBJECT, OFF(_upgrades), RO, 0},
1105 {"_conflicts", T_OBJECT, OFF(_conflicts), RO, 0},
1106 {"_objmap", T_OBJECT, OFF(_objmap), RO, 0},
1107diff --git a/smart/commands/query.py b/smart/commands/query.py
1108index 808e53a..9265cd9 100644
1109--- a/smart/commands/query.py
1110+++ b/smart/commands/query.py
1111@@ -107,6 +107,8 @@ def option_parser(**kwargs):
1112 help=_("show requires for the given packages"))
1113 parser.add_option("--show-prerequires", action="store_true",
1114 help=_("show requires selecting only pre-dependencies"))
1115+ parser.add_option("--show-recommends", action="store_true",
1116+ help=_("show recommends for the given packages"))
1117 parser.add_option("--show-upgrades", action="store_true",
1118 help=_("show upgrades for the given packages"))
1119 parser.add_option("--show-conflicts", action="store_true",
1120@@ -488,6 +490,19 @@ def main(ctrl, opts, reloadchannels=True):
1121 continue
1122 output.showRequiresProvidedBy(pkg, req,
1123 prv, prvpkg)
1124+ if pkg.recommends and (opts.show_recommends):
1125+ pkg.recommends.sort()
1126+ first = True
1127+ for req in pkg.recommends:
1128+ output.showRecommends(pkg, req)
1129+ if opts.show_providedby and req.providedby:
1130+ for prv in req.providedby:
1131+ prv.packages.sort()
1132+ for prvpkg in prv.packages:
1133+ if opts.installed and not prvpkg.installed:
1134+ continue
1135+ output.showRecommendsProvidedBy(pkg, req,
1136+ prv, prvpkg)
1137 if pkg.upgrades and (opts.show_upgrades or whoupgrades):
1138 pkg.upgrades.sort()
1139 first = True
1140@@ -594,6 +609,12 @@ class NullOutput(object):
1141 def showRequiresProvidedBy(self, pkg, req, prv, prvpkg):
1142 pass
1143
1144+ def showRecommends(self, pkg, req):
1145+ pass
1146+
1147+ def showRecommendsProvidedBy(self, pkg, req, prv, prvpkg):
1148+ pass
1149+
1150 def showUpgrades(self, pkg, upg):
1151 pass
1152
1153@@ -619,6 +640,8 @@ class TextOutput(NullOutput):
1154 self._firstconflictedby = True
1155 self._firstrequires = True
1156 self._firstrequiresprovidedby = True
1157+ self._firstrecommends = True
1158+ self._firstrecommendsprovidedby = True
1159 self._firstupgrades = True
1160 self._firstupgradesprovidedby = True
1161 self._firstconflicts = True
1162@@ -711,6 +734,22 @@ class TextOutput(NullOutput):
1163 name = str(prvpkg)
1164 print " ", "%s (%s)" % (name, prv)
1165
1166+ def showRecommends(self, pkg, rec):
1167+ if self._firstrecommends:
1168+ self._firstrecommends = False
1169+ print " ", _("Recommends:")
1170+ print " ", rec
1171+
1172+ def showRecommendsProvidedBy(self, pkg, req, prv, prvpkg):
1173+ if self._firstrecommendsprovidedby:
1174+ self._firstrecommendsprovidedby = False
1175+ print " ", _("Provided By:")
1176+ if self.opts.hide_version:
1177+ name = prvpkg.name
1178+ else:
1179+ name = str(prvpkg)
1180+ print " ", "%s (%s)" % (name, prv)
1181+
1182 def showUpgrades(self, pkg, upg):
1183 if self._firstupgrades:
1184 self._firstupgrades = False
1185@@ -797,6 +836,18 @@ class GraphVizOutput(NullOutput):
1186 self._shown[req, prv] = True
1187 print ' "Requires: %s" -> "Provides: %s";' % (req, prv)
1188
1189+ def showRecommends(self, pkg, req):
1190+ if (pkg, req) not in self._shown:
1191+ self._shown[pkg, req] = True
1192+ print ' "%s" -> "Recommends: %s";' % (pkg, req)
1193+
1194+ def showRecommendsProvidedBy(self, pkg, req, prv, prvpkg):
1195+ self.showPackage(prvpkg)
1196+ self.showProvides(prvpkg, prv)
1197+ if (req, prv) not in self._shown:
1198+ self._shown[req, prv] = True
1199+ print ' "Recommends: %s" -> "Provides: %s";' % (req, prv)
1200+
1201 def showUpgrades(self, pkg, upg):
1202 if (pkg, upg) not in self._shown:
1203 self._shown[pkg, upg] = True
1204diff --git a/smart/control.py b/smart/control.py
1205index fd7083a..d44abe7 100644
1206--- a/smart/control.py
1207+++ b/smart/control.py
1208@@ -447,7 +447,7 @@ class Control(object):
1209 queue = marked.keys()
1210 while queue:
1211 pkg = queue.pop(0)
1212- for req in pkg.requires:
1213+ for req in pkg.requires + pkg.recommends:
1214 for prv in req.providedby:
1215 for prvpkg in prv.packages:
1216 if (prvpkg.installed and
1217@@ -794,7 +794,7 @@ class Control(object):
1218 pkglst = []
1219 for pkg in changeset:
1220 n = 0
1221- for req in pkg.requires:
1222+ for req in pkg.requires + pkg.recommends:
1223 for prv in req.providedby:
1224 for prvpkg in prv.packages:
1225 if changeset.get(prvpkg) is INSTALL:
1226diff --git a/smart/searcher.py b/smart/searcher.py
1227index 216f4ce..32eb825 100644
1228--- a/smart/searcher.py
1229+++ b/smart/searcher.py
1230@@ -45,9 +45,9 @@ class Searcher(object):
1231
1232 - provides is matched in Provides.search(), for the same reason.
1233
1234- - requires, upgrades, and conflicts don't have special searching
1235- methods. Instead, their usual match() method is given an instance
1236- of the Provides type.
1237+ - requires, recommends, upgrades, and conflicts don't have special
1238+ searching methods. Instead, their usual match() method is given
1239+ an instance of the Provides type.
1240
1241 - group, path, url, and other information which is found by
1242 PackageInfo, is searched by the Loader.search() method and
1243@@ -62,6 +62,7 @@ class Searcher(object):
1244 self.nameversion = []
1245 self.provides = []
1246 self.requires = []
1247+ self.recommends = []
1248 self.upgrades = []
1249 self.conflicts = []
1250 self.path = []
1251@@ -76,6 +77,7 @@ class Searcher(object):
1252 del self.nameversion[:]
1253 del self.provides[:]
1254 del self.requires[:]
1255+ del self.recommends[:]
1256 del self.upgrades[:]
1257 del self.conflicts[:]
1258 del self.path[:]
1259@@ -122,6 +124,8 @@ class Searcher(object):
1260 self.addProvides(s[9:], cutoff)
1261 elif s.startswith("requires:"):
1262 self.addRequires(s[9:])
1263+ elif s.startswith("recommends:"):
1264+ self.addRecommends(s[11:])
1265 elif s.startswith("upgrades:"):
1266 self.addUpgrades(s[9:])
1267 elif s.startswith("conflicts:"):
1268@@ -151,6 +155,7 @@ class Searcher(object):
1269 return s and (
1270 s.startswith("provides:") or
1271 s.startswith("requires:") or
1272+ s.startswith("recommends:") or
1273 s.startswith("upgrades:") or
1274 s.startswith("conflicts:") or
1275 s.startswith("url:") or
1276@@ -182,6 +187,9 @@ class Searcher(object):
1277 def addRequires(self, s):
1278 self.requires.append(self._buildProvides(s))
1279
1280+ def addRecommends(self, s):
1281+ self.recommends.append(self._buildProvides(s))
1282+
1283 def addUpgrades(self, s):
1284 self.upgrades.append(self._buildProvides(s))
1285
1286diff --git a/smart/transaction.py b/smart/transaction.py
1287index eb320d2..300b9cc 100644
1288--- a/smart/transaction.py
1289+++ b/smart/transaction.py
1290@@ -573,7 +573,7 @@ class Transaction(object):
1291 self._remove(namepkg, changeset, locked, pending, depth)
1292
1293 # Install packages required by this one.
1294- for req in pkg.requires:
1295+ for req in pkg.requires + pkg.recommends:
1296
1297 # Check if someone is already providing it.
1298 prvpkgs = {}
1299@@ -596,8 +596,12 @@ class Transaction(object):
1300
1301 if not prvpkgs:
1302 # No packages provide it at all. Give up.
1303- raise Failed, _("Can't install %s: no package provides %s") % \
1304- (pkg, req)
1305+ if req in pkg.requires:
1306+ raise Failed, _("Can't install %s: no package provides %s") % \
1307+ (pkg, req)
1308+ else:
1309+ # It's only a recommend, skip
1310+ continue
1311
1312 if len(prvpkgs) == 1:
1313 # Don't check locked here. prvpkgs was
1314@@ -1359,7 +1363,7 @@ class ChangeSetSplitter(object):
1315 set = self._changeset
1316
1317 # Check all dependencies needed by this package.
1318- for req in pkg.requires:
1319+ for req in pkg.requires + pkg.recommends:
1320
1321 # Check if any already installed or to be installed
1322 # package will solve the problem.
1323@@ -1424,8 +1428,9 @@ class ChangeSetSplitter(object):
1324
1325 # There are no solutions for the problem.
1326 # Should we really care about it?
1327- if (self._forcerequires or
1328- isinstance(req, PreRequires)):
1329+ if ((self._forcerequires or
1330+ isinstance(req, PreRequires))
1331+ and req in pkg.requires):
1332 raise Error, _("No providers for '%s', "
1333 "required by '%s'") % (req, pkg)
1334
1335@@ -1625,7 +1630,7 @@ def recursiveInternalRequires(pkgmap, pkg, numrel, done=None):
1336 return n
1337
1338 def forwardRequires(pkg, map):
1339- for req in pkg.requires:
1340+ for req in pkg.requires + pkg.recommends:
1341 if req not in map:
1342 map[req] = True
1343 for prv in req.providedby:
1344@@ -1794,6 +1799,15 @@ def checkPackages(cache, checkset, relateset, report=False):
1345 iface.info(_("Unsatisfied dependency: %s requires %s") %
1346 (pkg, req))
1347
1348+ for req in pkg.recommends:
1349+ for prv in req.providedby:
1350+ for prvpkg in prv.packages:
1351+ if prvpkg in relateset:
1352+ break
1353+ else:
1354+ continue
1355+ break
1356+
1357 if not pkg.installed:
1358 continue
1359
1360--
13611.7.9.5
1362