summaryrefslogtreecommitdiffstats
path: root/meta/recipes-devtools/python
diff options
context:
space:
mode:
authorPaul Eggleton <paul.eggleton@linux.intel.com>2012-11-14 18:46:56 +0000
committerRichard Purdie <richard.purdie@linuxfoundation.org>2012-12-06 12:31:05 +0000
commit69b9c6bd0c6f9c9884fa2d92b4062037457d06ad (patch)
tree23f7d595b10a955d2f8b3c786c989c887a0f9300 /meta/recipes-devtools/python
parent5443525de8c209c27eab0457c9a5a7ca11ec9865 (diff)
downloadpoky-69b9c6bd0c6f9c9884fa2d92b4062037457d06ad.tar.gz
python-smartpm: add support for recommends
Implement support within Smart for handling RRECOMMENDS relationships between RPM packages as used by OE. This includes support within the base system for caching and resolving these relationships as well as specific support in the RPM backend for reading the information from packages, and reading the "missingok" flag added to createrepo for rpm-md feeds. (From OE-Core rev: 35e3bc90ec3bae824804bd176b3128efdb5b4e2b) Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com> Signed-off-by: Saul Wold <sgw@linux.intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta/recipes-devtools/python')
-rw-r--r--meta/recipes-devtools/python/python-smartpm/smart-missingok.patch43
-rw-r--r--meta/recipes-devtools/python/python-smartpm/smart-recommends.patch1362
-rw-r--r--meta/recipes-devtools/python/python-smartpm_1.4.1.bb2
3 files changed, 1363 insertions, 44 deletions
diff --git a/meta/recipes-devtools/python/python-smartpm/smart-missingok.patch b/meta/recipes-devtools/python/python-smartpm/smart-missingok.patch
deleted file mode 100644
index 7e138696b8..0000000000
--- a/meta/recipes-devtools/python/python-smartpm/smart-missingok.patch
+++ /dev/null
@@ -1,43 +0,0 @@
1backends/rpm: Identify recommended packages
2
3We identify and store recommended packages (and later throw that data away.)
4
5This is indended to be the starting work to add support for recommended
6packages to smart.
7
8Upstream-status: Inappropriate [ Code isn't finished! ]
9
10Signed-off-by: Mark Hatle <mark.hatle@windriver.com>
11
12Index: smart-1.4.1/smart/backends/rpm/header.py
13===================================================================
14--- smart-1.4.1.orig/smart/backends/rpm/header.py
15+++ smart-1.4.1/smart/backends/rpm/header.py
16@@ -292,6 +292,7 @@ class RPMHeaderLoader(Loader):
17 f = [0]
18 elif type(f) != list:
19 f = [f]
20+ recdict = {}
21 reqdict = {}
22 for i in range(len(n)):
23 ni = n[i]
24@@ -308,10 +309,17 @@ class RPMHeaderLoader(Loader):
25 # RPMSENSE_SCRIPT_PREUN |
26 # RPMSENSE_SCRIPT_POST |
27 # RPMSENSE_SCRIPT_POSTUN == 7744
28- reqdict[(f[i]&7744 and PreReq or Req,
29- intern(ni), r, vi)] = True
30+ if (f[i]&rpm.RPMSENSE_MISSINGOK):
31+ print "Ignoring Recommend Dependency: %s" % (ni)
32+ recdict[(f[i]&7744 and PreReq or Req,
33+ intern(ni), r, vi)] = True
34+ else:
35+ reqdict[(f[i]&7744 and PreReq or Req,
36+ intern(ni), r, vi)] = True
37+ recargs = collapse_libc_requires(recdict.keys())
38 reqargs = collapse_libc_requires(reqdict.keys())
39 else:
40+ recargs = None
41 reqargs = None
42
43 n = h[1054] # RPMTAG_CONFLICTNAME
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
diff --git a/meta/recipes-devtools/python/python-smartpm_1.4.1.bb b/meta/recipes-devtools/python/python-smartpm_1.4.1.bb
index 254318c285..9048bc8f02 100644
--- a/meta/recipes-devtools/python/python-smartpm_1.4.1.bb
+++ b/meta/recipes-devtools/python/python-smartpm_1.4.1.bb
@@ -18,7 +18,7 @@ SRC_URI = "\
18 http://launchpad.net/smart/trunk/${PV}/+download/${SRCNAME}-${PV}.tar.bz2 \ 18 http://launchpad.net/smart/trunk/${PV}/+download/${SRCNAME}-${PV}.tar.bz2 \
19 file://smartpm-rpm5-nodig.patch \ 19 file://smartpm-rpm5-nodig.patch \
20 file://smart-rpm-root.patch \ 20 file://smart-rpm-root.patch \
21 file://smart-missingok.patch \ 21 file://smart-recommends.patch \
22 " 22 "
23 23
24SRC_URI[md5sum] = "573ef32ba177a6b3c4bf7ef04873fcb6" 24SRC_URI[md5sum] = "573ef32ba177a6b3c4bf7ef04873fcb6"