From 972dcfcdbfe75dcfeb777150c136576cf1a71e99 Mon Sep 17 00:00:00 2001 From: Tudor Florea Date: Fri, 9 Oct 2015 22:59:03 +0200 Subject: initial commit for Enea Linux 5.0 arm Signed-off-by: Tudor Florea --- .../python/python-smartpm/smart-recommends.patch | 1362 ++++++++++++++++++++ 1 file changed, 1362 insertions(+) create mode 100644 meta/recipes-devtools/python/python-smartpm/smart-recommends.patch (limited to 'meta/recipes-devtools/python/python-smartpm/smart-recommends.patch') 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 @@ +Handle recommended packages in core and rpm backends + +Identify and store recommended packages in the cache, add a query option +to read them and ignore them if they are not present when installing. + +Initial identification code from Mark Hatle . + +Upstream-Status: Pending + +Signed-off-by: Paul Eggleton + +diff --git a/smart/backends/rpm/base.py b/smart/backends/rpm/base.py +index 0489e11..b9e9cb2 100644 +--- a/smart/backends/rpm/base.py ++++ b/smart/backends/rpm/base.py +@@ -198,6 +198,29 @@ class RPMPackage(Package): + break + else: + return False ++ srecs = fk(self.recommends) ++ orecs = fk(other.recommends) ++ if srecs != orecs: ++ for srec in srecs: ++ if srec.name[0] == "/" or srec in orecs: ++ continue ++ for orec in orecs: ++ if (srec.name == orec.name and ++ srec.relation == orec.relation and ++ checkver(srec.version, orec.version)): ++ break ++ else: ++ return False ++ for orec in orecs: ++ if orec.name[0] == "/" or orec in srecs: ++ continue ++ for srec in srecs: ++ if (srec.name == orec.name and ++ srec.relation == orec.relation and ++ checkver(srec.version, orec.version)): ++ break ++ else: ++ return False + return True + + def coexists(self, other): +diff --git a/smart/backends/rpm/header.py b/smart/backends/rpm/header.py +index 31786cc..4880f43 100644 +--- a/smart/backends/rpm/header.py ++++ b/smart/backends/rpm/header.py +@@ -292,6 +292,7 @@ class RPMHeaderLoader(Loader): + f = [0] + elif type(f) != list: + f = [f] ++ recdict = {} + reqdict = {} + for i in range(len(n)): + ni = n[i] +@@ -308,10 +309,16 @@ class RPMHeaderLoader(Loader): + # RPMSENSE_SCRIPT_PREUN | + # RPMSENSE_SCRIPT_POST | + # RPMSENSE_SCRIPT_POSTUN == 7744 +- reqdict[(f[i]&7744 and PreReq or Req, +- intern(ni), r, vi)] = True ++ if (f[i]&rpm.RPMSENSE_MISSINGOK): ++ recdict[(f[i]&7744 and PreReq or Req, ++ intern(ni), r, vi)] = True ++ else: ++ reqdict[(f[i]&7744 and PreReq or Req, ++ intern(ni), r, vi)] = True ++ recargs = collapse_libc_requires(recdict.keys()) + reqargs = collapse_libc_requires(reqdict.keys()) + else: ++ recargs = None + reqargs = None + + n = h[1054] # RPMTAG_CONFLICTNAME +@@ -365,7 +372,7 @@ class RPMHeaderLoader(Loader): + versionarch = "%s@%s" % (distversion, arch) + + pkg = self.buildPackage((Pkg, name, versionarch), +- prvargs, reqargs, upgargs, cnfargs) ++ prvargs, reqargs, upgargs, cnfargs, recargs) + pkg.loaders[self] = offset + self._offsets[offset] = pkg + self._groups[pkg] = intern(h[rpm.RPMTAG_GROUP]) +@@ -583,8 +590,8 @@ class URPMILoader(RPMHeaderListLoader): + def setErrataFlags(self, flagdict): + self._flagdict = flagdict + +- def buildPackage(self, pkgargs, prvargs, reqargs, upgargs, cnfargs): +- pkg = Loader.buildPackage(self, pkgargs, prvargs, reqargs, upgargs, cnfargs) ++ def buildPackage(self, pkgargs, prvargs, reqargs, upgargs, cnfargs, recargs): ++ pkg = Loader.buildPackage(self, pkgargs, prvargs, reqargs, upgargs, cnfargs, recargs) + name = pkgargs[1] + if hasattr(self, '_flagdict') and self._flagdict and name in self._flagdict: + if sysconf.getReadOnly(): +diff --git a/smart/backends/rpm/metadata.py b/smart/backends/rpm/metadata.py +index 2c54f39..568fe06 100644 +--- a/smart/backends/rpm/metadata.py ++++ b/smart/backends/rpm/metadata.py +@@ -165,6 +165,7 @@ class RPMMetaDataLoader(Loader): + distepoch = None + info = {} + reqdict = {} ++ recdict = {} + prvdict = {} + upgdict = {} + cnfdict = {} +@@ -287,12 +288,16 @@ class RPMMetaDataLoader(Loader): + + lasttag = queue[-1].tag + if lasttag == REQUIRES: +- if elem.get("pre") == "1": +- reqdict[(RPMPreRequires, +- ename, erelation, eversion)] = True ++ if elem.get("missingok") == "1": ++ recdict[(RPMRequires, ++ ename, erelation, eversion)] = True + else: +- reqdict[(RPMRequires, +- ename, erelation, eversion)] = True ++ if elem.get("pre") == "1": ++ reqdict[(RPMPreRequires, ++ ename, erelation, eversion)] = True ++ else: ++ reqdict[(RPMRequires, ++ ename, erelation, eversion)] = True + + elif lasttag == PROVIDES: + if ename[0] == "/": +@@ -328,6 +333,12 @@ class RPMMetaDataLoader(Loader): + (RPMProvides, x[1], x[3]) in prvdict or + system_provides.match(*x[:3]))] + reqargs = collapse_libc_requires(reqargs) ++ ++ recargs = [x for x in recdict ++ if not ((x[2] is None or "=" in x[2]) and ++ (RPMProvides, x[1], x[3]) in prvdict or ++ system_provides.match(*x[:3]))] ++ + prvargs = prvdict.keys() + cnfargs = cnfdict.keys() + upgargs = upgdict.keys() +@@ -339,7 +350,7 @@ class RPMMetaDataLoader(Loader): + versionarch = "%s@%s" % (distversion, arch) + + pkg = self.buildPackage((RPMPackage, name, versionarch), +- prvargs, reqargs, upgargs, cnfargs) ++ prvargs, reqargs, upgargs, cnfargs, recargs) + pkg.loaders[self] = info + + # Store the provided files for future usage. +@@ -362,6 +373,7 @@ class RPMMetaDataLoader(Loader): + distepoch = None + pkgid = None + reqdict.clear() ++ recdict.clear() + prvdict.clear() + upgdict.clear() + cnfdict.clear() +diff --git a/smart/cache.py b/smart/cache.py +index b829825..cec8bb3 100644 +--- a/smart/cache.py ++++ b/smart/cache.py +@@ -32,7 +32,8 @@ class Package(object): + self.name = name + self.version = version + self.provides = () +- self.requires = () ++ self.requires = [] ++ self.recommends = [] + self.upgrades = () + self.conflicts = () + self.installed = False +@@ -55,7 +56,9 @@ class Package(object): + fk([x for x in self.provides if x.name[0] != "/"]) != + fk([x for x in other.provides if x.name[0] != "/"]) or + fk([x for x in self.requires if x.name[0] != "/"]) != +- fk([x for x in other.requires if x.name[0] != "/"])): ++ fk([x for x in other.requires if x.name[0] != "/"]) or ++ fk([x for x in self.recommends if x.name[0] != "/"]) != ++ fk([x for x in other.recommends if x.name[0] != "/"])): + return False + return True + +@@ -110,6 +113,7 @@ class Package(object): + self.version, + self.provides, + self.requires, ++ self.recommends, + self.upgrades, + self.conflicts, + self.installed, +@@ -122,6 +126,7 @@ class Package(object): + self.version, + self.provides, + self.requires, ++ self.recommends, + self.upgrades, + self.conflicts, + self.installed, +@@ -274,6 +279,7 @@ class Provides(object): + self.version = version + self.packages = [] + self.requiredby = () ++ self.recommendedby = () + self.upgradedby = () + self.conflictedby = () + +@@ -401,7 +407,7 @@ class Loader(object): + def loadFileProvides(self, fndict): + pass + +- def buildPackage(self, pkgargs, prvargs, reqargs, upgargs, cnfargs): ++ def buildPackage(self, pkgargs, prvargs, reqargs, upgargs, cnfargs, recargs = None): + cache = self._cache + pkg = pkgargs[0](*pkgargs[1:]) + relpkgs = [] +@@ -427,6 +433,17 @@ class Loader(object): + relpkgs.append(req.packages) + pkg.requires.append(req) + ++ if recargs: ++ pkg.recommends = [] ++ for args in recargs: ++ rec = cache._objmap.get(args) ++ if not rec: ++ rec = args[0](*args[1:]) ++ cache._objmap[args] = rec ++ cache._recommends.append(rec) ++ relpkgs.append(rec.packages) ++ pkg.recommends.append(rec) ++ + if upgargs: + pkg.upgrades = [] + for args in upgargs: +@@ -572,6 +589,7 @@ class Cache(object): + self._packages = [] + self._provides = [] + self._requires = [] ++ self._recommends = [] + self._upgrades = [] + self._conflicts = [] + self._objmap = {} +@@ -581,6 +599,8 @@ class Cache(object): + del prv.packages[:] + if prv.requiredby: + del prv.requiredby[:] ++ if prv.recommendedby: ++ del prv.recommendedby[:] + if prv.upgradedby: + del prv.upgradedby[:] + if prv.conflictedby: +@@ -589,6 +609,10 @@ class Cache(object): + del req.packages[:] + if req.providedby: + del req.providedby[:] ++ for rec in self._recommends: ++ del rec.packages[:] ++ if rec.providedby: ++ del rec.providedby[:] + for upg in self._upgrades: + del upg.packages[:] + if upg.providedby: +@@ -600,6 +624,7 @@ class Cache(object): + del self._packages[:] + del self._provides[:] + del self._requires[:] ++ del self._recommends[:] + del self._upgrades[:] + del self._conflicts[:] + self._objmap.clear() +@@ -621,6 +646,7 @@ class Cache(object): + packages = {} + provides = {} + requires = {} ++ recommends = {} + upgrades = {} + conflicts = {} + objmap = self._objmap +@@ -646,6 +672,11 @@ class Cache(object): + if req not in requires: + objmap[req.getInitArgs()] = req + requires[req] = True ++ for rec in pkg.recommends[:]: ++ rec.packages.append(pkg) ++ if rec not in recommends: ++ objmap[rec.getInitArgs()] = rec ++ recommends[rec] = True + for upg in pkg.upgrades: + upg.packages.append(pkg) + if upg not in upgrades: +@@ -659,6 +690,7 @@ class Cache(object): + self._packages[:] = packages.keys() + self._provides[:] = provides.keys() + self._requires[:] = requires.keys() ++ self._recommends[:] = recommends.keys() + self._upgrades[:] = upgrades.keys() + self._conflicts[:] = conflicts.keys() + +@@ -710,6 +742,14 @@ class Cache(object): + lst.append(req) + else: + reqnames[name] = [req] ++ recnames = {} ++ for rec in self._recommends: ++ for name in rec.getMatchNames(): ++ lst = recnames.get(name) ++ if lst: ++ lst.append(rec) ++ else: ++ recnames[name] = [rec] + upgnames = {} + for upg in self._upgrades: + for name in upg.getMatchNames(): +@@ -739,6 +779,18 @@ class Cache(object): + prv.requiredby.append(req) + else: + prv.requiredby = [req] ++ lst = recnames.get(prv.name) ++ if lst: ++ for rec in lst: ++ if rec.matches(prv): ++ if rec.providedby: ++ rec.providedby.append(prv) ++ else: ++ rec.providedby = [prv] ++ if prv.recommendedby: ++ prv.recommendedby.append(rec) ++ else: ++ prv.recommendedby = [rec] + lst = upgnames.get(prv.name) + if lst: + for upg in lst: +@@ -782,6 +834,12 @@ class Cache(object): + else: + return [x for x in self._requires if x.name == name] + ++ def getRecommends(self, name=None): ++ if not name: ++ return self._recommends ++ else: ++ return [x for x in self._recommends if x.name == name] ++ + def getUpgrades(self, name=None): + if not name: + return self._upgrades +@@ -807,6 +865,12 @@ class Cache(object): + for req in self._requires: + if prvname in req.getMatchNames() and req.matches(prv): + searcher.addResult(req) ++ if searcher.recommends: ++ for prv in searcher.recommends: ++ prvname = prv.name ++ for req in self._recommends: ++ if prvname in req.getMatchNames() and req.matches(prv): ++ searcher.addResult(req) + if searcher.upgrades: + for prv in searcher.upgrades: + prvname = prv.name +@@ -839,6 +903,7 @@ class Cache(object): + self._packages = state["_packages"] + provides = {} + requires = {} ++ recommends = {} + upgrades = {} + conflicts = {} + for pkg in self._packages: +@@ -848,6 +913,9 @@ class Cache(object): + for req in pkg.requires: + req.packages.append(pkg) + requires[req] = True ++ for rec in pkg.recommends: ++ rec.packages.append(pkg) ++ recommends[rec] = True + for upg in pkg.upgrades: + upg.packages.append(pkg) + upgrades[upg] = True +@@ -856,6 +924,7 @@ class Cache(object): + conflicts[cnf] = True + self._provides = provides.keys() + self._requires = requires.keys() ++ self._recommends = recommends.keys() + self._upgrades = upgrades.keys() + self._conflicts = conflicts.keys() + self._objmap = {} +diff --git a/smart/ccache.c b/smart/ccache.c +index 7541e26..7193185 100644 +--- a/smart/ccache.c ++++ b/smart/ccache.c +@@ -82,6 +82,7 @@ typedef struct { + PyObject *version; + PyObject *provides; + PyObject *requires; ++ PyObject *recommends; + PyObject *upgrades; + PyObject *conflicts; + PyObject *installed; +@@ -96,6 +97,7 @@ typedef struct { + PyObject *version; + PyObject *packages; + PyObject *requiredby; ++ PyObject *recommendedby; + PyObject *upgradedby; + PyObject *conflictedby; + } ProvidesObject; +@@ -123,6 +125,7 @@ typedef struct { + PyObject *_packages; + PyObject *_provides; + PyObject *_requires; ++ PyObject *_recommends; + PyObject *_upgrades; + PyObject *_conflicts; + PyObject *_objmap; +@@ -211,7 +214,8 @@ Package_init(PackageObject *self, PyObject *args) + Py_INCREF(self->name); + Py_INCREF(self->version); + self->provides = PyTuple_New(0); +- self->requires = PyTuple_New(0); ++ self->requires = PyList_New(0); ++ self->recommends = PyList_New(0); + self->upgrades = PyTuple_New(0); + self->conflicts = PyTuple_New(0); + Py_INCREF(Py_False); +@@ -228,6 +232,7 @@ Package_traverse(PackageObject *self, visitproc visit, void *arg) + { + Py_VISIT(self->provides); + Py_VISIT(self->requires); ++ Py_VISIT(self->recommends); + Py_VISIT(self->upgrades); + Py_VISIT(self->conflicts); + Py_VISIT(self->loaders); +@@ -239,6 +244,7 @@ Package_clear(PackageObject *self) + { + Py_CLEAR(self->provides); + Py_CLEAR(self->requires); ++ Py_CLEAR(self->recommends); + Py_CLEAR(self->upgrades); + Py_CLEAR(self->conflicts); + Py_CLEAR(self->loaders); +@@ -252,6 +258,7 @@ Package_dealloc(PackageObject *self) + Py_XDECREF(self->version); + Py_XDECREF(self->provides); + Py_XDECREF(self->requires); ++ Py_XDECREF(self->recommends); + Py_XDECREF(self->upgrades); + Py_XDECREF(self->conflicts); + Py_XDECREF(self->installed); +@@ -453,6 +460,46 @@ Package_equals(PackageObject *self, PackageObject *other) + } + } + ++ ilen = 0; ++ jlen = 0; ++ for (i = 0; i != PyList_GET_SIZE(self->recommends); i++) { ++ PyObject *item = PyList_GET_ITEM(self->recommends, i); ++ if (!PyObject_IsInstance(item, (PyObject *)&Depends_Type)) { ++ PyErr_SetString(PyExc_TypeError, "Depends instance expected"); ++ return NULL; ++ } ++ if (STR(((DependsObject *)item)->name)[0] != '/') ++ ilen += 1; ++ } ++ for (j = 0; j != PyList_GET_SIZE(other->recommends); j++) { ++ PyObject *item = PyList_GET_ITEM(other->recommends, j); ++ if (!PyObject_IsInstance(item, (PyObject *)&Depends_Type)) { ++ PyErr_SetString(PyExc_TypeError, "Depends instance expected"); ++ return NULL; ++ } ++ if (STR(((DependsObject *)item)->name)[0] != '/') ++ jlen += 1; ++ } ++ if (ilen != jlen) { ++ ret = Py_False; ++ goto exit; ++ } ++ ++ ilen = PyList_GET_SIZE(self->recommends); ++ jlen = PyList_GET_SIZE(other->recommends); ++ for (i = 0; i != ilen; i++) { ++ PyObject *item = PyList_GET_ITEM(self->recommends, i); ++ if (STR(((DependsObject *)item)->name)[0] != '/') { ++ for (j = 0; j != jlen; j++) ++ if (item == PyList_GET_ITEM(other->recommends, j)) ++ break; ++ if (j == jlen) { ++ ret = Py_False; ++ goto exit; ++ } ++ } ++ } ++ + exit: + Py_INCREF(ret); + return ret; +@@ -606,13 +653,14 @@ Package_getPriority(PackageObject *self, PyObject *args) + static PyObject * + Package__getstate__(PackageObject *self, PyObject *args) + { +- PyObject *state = PyTuple_New(10); ++ PyObject *state = PyTuple_New(11); + if (!state) return NULL; + + Py_INCREF(self->name); + Py_INCREF(self->version); + Py_INCREF(self->provides); + Py_INCREF(self->requires); ++ Py_INCREF(self->recommends); + Py_INCREF(self->upgrades); + Py_INCREF(self->conflicts); + Py_INCREF(self->installed); +@@ -620,16 +668,17 @@ Package__getstate__(PackageObject *self, PyObject *args) + Py_INCREF(self->priority); + Py_INCREF(self->loaders); + +- PyTuple_SET_ITEM(state, 0, self->name); +- PyTuple_SET_ITEM(state, 1, self->version); +- PyTuple_SET_ITEM(state, 2, self->provides); +- PyTuple_SET_ITEM(state, 3, self->requires); +- PyTuple_SET_ITEM(state, 4, self->upgrades); +- PyTuple_SET_ITEM(state, 5, self->conflicts); +- PyTuple_SET_ITEM(state, 6, self->installed); +- PyTuple_SET_ITEM(state, 7, self->essential); +- PyTuple_SET_ITEM(state, 8, self->priority); +- PyTuple_SET_ITEM(state, 9, self->loaders); ++ PyTuple_SET_ITEM(state, 0, self->name); ++ PyTuple_SET_ITEM(state, 1, self->version); ++ PyTuple_SET_ITEM(state, 2, self->provides); ++ PyTuple_SET_ITEM(state, 3, self->requires); ++ PyTuple_SET_ITEM(state, 4, self->recommends); ++ PyTuple_SET_ITEM(state, 5, self->upgrades); ++ PyTuple_SET_ITEM(state, 6, self->conflicts); ++ PyTuple_SET_ITEM(state, 7, self->installed); ++ PyTuple_SET_ITEM(state, 8, self->essential); ++ PyTuple_SET_ITEM(state, 9, self->priority); ++ PyTuple_SET_ITEM(state, 10, self->loaders); + + return state; + } +@@ -637,7 +686,7 @@ Package__getstate__(PackageObject *self, PyObject *args) + static PyObject * + Package__setstate__(PackageObject *self, PyObject *state) + { +- if (!PyTuple_Check(state) || PyTuple_GET_SIZE(state) != 10) { ++ if (!PyTuple_Check(state) || PyTuple_GET_SIZE(state) != 11) { + PyErr_SetString(StateVersionError, ""); + return NULL; + } +@@ -645,18 +694,20 @@ Package__setstate__(PackageObject *self, PyObject *state) + self->version = PyTuple_GET_ITEM(state, 1); + self->provides = PyTuple_GET_ITEM(state, 2); + self->requires = PyTuple_GET_ITEM(state, 3); +- self->upgrades = PyTuple_GET_ITEM(state, 4); +- self->conflicts = PyTuple_GET_ITEM(state, 5); +- self->installed = PyTuple_GET_ITEM(state, 6); +- self->essential = PyTuple_GET_ITEM(state, 7); +- self->priority = PyTuple_GET_ITEM(state, 8); +- self->loaders = PyTuple_GET_ITEM(state, 9); ++ self->recommends = PyTuple_GET_ITEM(state, 4); ++ self->upgrades = PyTuple_GET_ITEM(state, 5); ++ self->conflicts = PyTuple_GET_ITEM(state, 6); ++ self->installed = PyTuple_GET_ITEM(state, 7); ++ self->essential = PyTuple_GET_ITEM(state, 8); ++ self->priority = PyTuple_GET_ITEM(state, 9); ++ self->loaders = PyTuple_GET_ITEM(state, 10); + + + Py_INCREF(self->name); + Py_INCREF(self->version); + Py_INCREF(self->provides); + Py_INCREF(self->requires); ++ Py_INCREF(self->recommends); + Py_INCREF(self->upgrades); + Py_INCREF(self->conflicts); + Py_INCREF(self->installed); +@@ -686,6 +737,7 @@ static PyMemberDef Package_members[] = { + {"version", T_OBJECT, OFF(version), 0, 0}, + {"provides", T_OBJECT, OFF(provides), 0, 0}, + {"requires", T_OBJECT, OFF(requires), 0, 0}, ++ {"recommends", T_OBJECT, OFF(recommends), 0, 0}, + {"upgrades", T_OBJECT, OFF(upgrades), 0, 0}, + {"conflicts", T_OBJECT, OFF(conflicts), 0, 0}, + {"installed", T_OBJECT, OFF(installed), 0, 0}, +@@ -750,6 +802,7 @@ Provides_init(ProvidesObject *self, PyObject *args) + Py_INCREF(self->version); + self->packages = PyList_New(0); + self->requiredby = PyTuple_New(0); ++ self->recommendedby = PyTuple_New(0); + self->upgradedby = PyTuple_New(0); + self->conflictedby = PyTuple_New(0); + return 0; +@@ -760,6 +813,7 @@ Provides_traverse(ProvidesObject *self, visitproc visit, void *arg) + { + Py_VISIT(self->packages); + Py_VISIT(self->requiredby); ++ Py_VISIT(self->recommendedby); + Py_VISIT(self->upgradedby); + Py_VISIT(self->conflictedby); + return 0; +@@ -770,6 +824,7 @@ Provides_clear(ProvidesObject *self) + { + Py_CLEAR(self->packages); + Py_CLEAR(self->requiredby); ++ Py_CLEAR(self->recommendedby); + Py_CLEAR(self->upgradedby); + Py_CLEAR(self->conflictedby); + return 0; +@@ -782,6 +837,7 @@ Provides_dealloc(ProvidesObject *self) + Py_XDECREF(self->version); + Py_XDECREF(self->packages); + Py_XDECREF(self->requiredby); ++ Py_XDECREF(self->recommendedby); + Py_XDECREF(self->upgradedby); + Py_XDECREF(self->conflictedby); + self->ob_type->tp_free((PyObject *)self); +@@ -960,6 +1016,7 @@ static PyMemberDef Provides_members[] = { + {"version", T_OBJECT, OFF(version), 0, 0}, + {"packages", T_OBJECT, OFF(packages), 0, 0}, + {"requiredby", T_OBJECT, OFF(requiredby), 0, 0}, ++ {"recommendedby", T_OBJECT, OFF(recommendedby), 0, 0}, + {"upgradedby", T_OBJECT, OFF(upgradedby), 0, 0}, + {"conflictedby", T_OBJECT, OFF(conflictedby), 0, 0}, + {NULL} +@@ -1555,6 +1612,7 @@ Loader_buildPackage(LoaderObject *self, PyObject *args) + PyObject *reqargs; + PyObject *upgargs; + PyObject *cnfargs; ++ PyObject *recargs = NULL; + PyObject *callargs; + + PyObject *pkg; +@@ -1574,9 +1632,10 @@ Loader_buildPackage(LoaderObject *self, PyObject *args) + + cache = (CacheObject *)self->_cache; + +- if (!PyArg_ParseTuple(args, "O!O&O&O&O&", &PyTuple_Type, &pkgargs, ++ if (!PyArg_ParseTuple(args, "O!O&O&O&O&|O&", &PyTuple_Type, &pkgargs, + mylist, &prvargs, mylist, &reqargs, +- mylist, &upgargs, mylist, &cnfargs)) ++ mylist, &upgargs, mylist, &cnfargs, ++ mylist, &recargs)) + return NULL; + + if (PyTuple_GET_SIZE(pkgargs) < 2) { +@@ -1701,6 +1760,59 @@ Loader_buildPackage(LoaderObject *self, PyObject *args) + } + } + ++ /* if recargs: */ ++ if (recargs) { ++ int i = 0; ++ int len = PyList_GET_SIZE(recargs); ++ /* pkg.recommends = [] */ ++ Py_DECREF(pkgobj->recommends); ++ pkgobj->recommends = PyList_New(len); ++ /* for args in recargs: */ ++ for (; i != len; i++) { ++ PyObject *args = PyList_GET_ITEM(recargs, i); ++ DependsObject *recobj; ++ PyObject *rec; ++ ++ if (!PyTuple_Check(args)) { ++ PyErr_SetString(PyExc_TypeError, ++ "Item in recargs is not a tuple"); ++ return NULL; ++ } ++ ++ /* rec = cache._objmap.get(args) */ ++ rec = PyDict_GetItem(cache->_objmap, args); ++ recobj = (DependsObject *)rec; ++ ++ /* if not rec: */ ++ if (!rec) { ++ if (!PyTuple_Check(args) || PyTuple_GET_SIZE(args) < 2) { ++ PyErr_SetString(PyExc_ValueError, "Invalid recargs tuple"); ++ return NULL; ++ } ++ /* rec = args[0](*args[1:]) */ ++ callargs = PyTuple_GetSlice(args, 1, PyTuple_GET_SIZE(args)); ++ rec = PyObject_CallObject(PyTuple_GET_ITEM(args, 0), callargs); ++ Py_DECREF(callargs); ++ if (!rec) return NULL; ++ recobj = (DependsObject *)rec; ++ ++ /* cache._objmap[args] = rec */ ++ PyDict_SetItem(cache->_objmap, args, rec); ++ Py_DECREF(rec); ++ ++ /* cache._recommends.append(rec) */ ++ PyList_Append(cache->_recommends, rec); ++ } ++ ++ /* relpkgs.append(rec.packages) */ ++ PyList_Append(relpkgs, recobj->packages); ++ ++ /* pkg.recommends.append(rec) */ ++ Py_INCREF(rec); ++ PyList_SET_ITEM(pkgobj->recommends, i, rec); ++ } ++ } ++ + /* if upgargs: */ + if (upgargs) { + int i = 0; +@@ -2391,6 +2503,7 @@ Cache_init(CacheObject *self, PyObject *args) + self->_packages = PyList_New(0); + self->_provides = PyList_New(0); + self->_requires = PyList_New(0); ++ self->_recommends = PyList_New(0); + self->_upgrades = PyList_New(0); + self->_conflicts = PyList_New(0); + self->_objmap = PyDict_New(); +@@ -2404,6 +2517,7 @@ Cache_traverse(CacheObject *self, visitproc visit, void *arg) + Py_VISIT(self->_packages); + Py_VISIT(self->_provides); + Py_VISIT(self->_requires); ++ Py_VISIT(self->_recommends); + Py_VISIT(self->_upgrades); + Py_VISIT(self->_conflicts); + Py_VISIT(self->_objmap); +@@ -2417,6 +2531,7 @@ Cache_clear(CacheObject *self) + Py_CLEAR(self->_packages); + Py_CLEAR(self->_provides); + Py_CLEAR(self->_requires); ++ Py_CLEAR(self->_recommends); + Py_CLEAR(self->_upgrades); + Py_CLEAR(self->_conflicts); + Py_CLEAR(self->_objmap); +@@ -2430,6 +2545,7 @@ Cache_dealloc(CacheObject *self) + Py_XDECREF(self->_packages); + Py_XDECREF(self->_provides); + Py_XDECREF(self->_requires); ++ Py_XDECREF(self->_recommends); + Py_XDECREF(self->_upgrades); + Py_XDECREF(self->_conflicts); + Py_XDECREF(self->_objmap); +@@ -2449,6 +2565,8 @@ Cache_reset(CacheObject *self, PyObject *args) + LIST_CLEAR(prvobj->packages); + if (PyList_Check(prvobj->requiredby)) + LIST_CLEAR(prvobj->requiredby); ++ if (PyList_Check(prvobj->recommendedby)) ++ LIST_CLEAR(prvobj->recommendedby); + if (PyList_Check(prvobj->upgradedby)) + LIST_CLEAR(prvobj->upgradedby); + if (PyList_Check(prvobj->conflictedby)) +@@ -2464,6 +2582,16 @@ Cache_reset(CacheObject *self, PyObject *args) + if (PyList_Check(reqobj->providedby)) + LIST_CLEAR(reqobj->providedby); + } ++ len = PyList_GET_SIZE(self->_recommends); ++ for (i = 0; i != len; i++) { ++ DependsObject *reqobj; ++ PyObject *req; ++ req = PyList_GET_ITEM(self->_recommends, i); ++ reqobj = (DependsObject *)req; ++ LIST_CLEAR(reqobj->packages); ++ if (PyList_Check(reqobj->providedby)) ++ LIST_CLEAR(reqobj->providedby); ++ } + len = PyList_GET_SIZE(self->_upgrades); + for (i = 0; i != len; i++) { + DependsObject *upgobj; +@@ -2487,6 +2615,7 @@ Cache_reset(CacheObject *self, PyObject *args) + LIST_CLEAR(self->_packages); + LIST_CLEAR(self->_provides); + LIST_CLEAR(self->_requires); ++ LIST_CLEAR(self->_recommends); + LIST_CLEAR(self->_upgrades); + LIST_CLEAR(self->_conflicts); + PyDict_Clear(self->_objmap); +@@ -2534,6 +2663,7 @@ Cache__reload(CacheObject *self, PyObject *args) + packages = {} + provides = {} + requires = {} ++ recommends = {} + upgrades = {} + conflicts = {} + objmap = self._objmap +@@ -2541,11 +2671,12 @@ Cache__reload(CacheObject *self, PyObject *args) + PyObject *packages = PyDict_New(); + PyObject *provides = PyDict_New(); + PyObject *requires = PyDict_New(); ++ PyObject *recommends = PyDict_New(); + PyObject *upgrades = PyDict_New(); + PyObject *conflicts = PyDict_New(); + PyObject *objmap = self->_objmap; + int i, ilen; +- if (!packages || !provides || !requires || !conflicts) ++ if (!packages || !provides || !requires || !recommends || !conflicts ) + return NULL; + + /* for loader in loaders: */ +@@ -2679,6 +2810,30 @@ Cache__reload(CacheObject *self, PyObject *args) + } + + /* ++ for rec in pkg.recommends: ++ rec.packages.append(pkg) ++ if rec not in recommends: ++ recommends[rec] = True ++ objmap[rec.getInitArgs()] = rec ++ */ ++ if (PyList_Check(pkg->recommends)) { ++ klen = PyList_GET_SIZE(pkg->recommends); ++ for (k = 0; k != klen; k++) { ++ PyObject *rec = PyList_GET_ITEM(pkg->recommends, k); ++ PyList_Append(((DependsObject *)rec)->packages, ++ (PyObject *)pkg); ++ if (!PyDict_GetItem(recommends, rec)) { ++ PyDict_SetItem(recommends, rec, Py_True); ++ args = PyObject_CallMethod(rec, "getInitArgs", ++ NULL); ++ if (!args) return NULL; ++ PyDict_SetItem(objmap, args, rec); ++ Py_DECREF(args); ++ } ++ } ++ } ++ ++ /* + for upg in pkg.upgrades: + upg.packages.append(pkg) + if upg not in upgrades: +@@ -2747,6 +2902,11 @@ Cache__reload(CacheObject *self, PyObject *args) + self->_requires = PyDict_Keys(requires); + Py_DECREF(requires); + ++ /* self._recommends[:] = recommends.keys() */ ++ Py_DECREF(self->_recommends); ++ self->_recommends = PyDict_Keys(recommends); ++ Py_DECREF(recommends); ++ + /* self._upgrades[:] = upgrades.keys() */ + Py_DECREF(self->_upgrades); + self->_upgrades = PyDict_Keys(upgrades); +@@ -2852,7 +3012,7 @@ PyObject * + Cache_linkDeps(CacheObject *self, PyObject *args) + { + int i, j, len; +- PyObject *reqnames, *upgnames, *cnfnames; ++ PyObject *reqnames, *recnames, *upgnames, *cnfnames; + PyObject *lst; + + /* reqnames = {} */ +@@ -2896,6 +3056,47 @@ Cache_linkDeps(CacheObject *self, PyObject *args) + Py_DECREF(seq); + } + ++ /* recnames = {} */ ++ recnames = PyDict_New(); ++ /* for rec in self._recommends: */ ++ len = PyList_GET_SIZE(self->_recommends); ++ for (i = 0; i != len; i++) { ++ PyObject *rec = PyList_GET_ITEM(self->_recommends, i); ++ ++ /* for name in rec.getMatchNames(): */ ++ PyObject *names = PyObject_CallMethod(rec, "getMatchNames", NULL); ++ PyObject *seq = PySequence_Fast(names, "getMatchNames() returned " ++ "non-sequence object"); ++ int nameslen; ++ if (!seq) return NULL; ++ nameslen = PySequence_Fast_GET_SIZE(seq); ++ for (j = 0; j != nameslen; j++) { ++ PyObject *name = PySequence_Fast_GET_ITEM(seq, j); ++ ++ /* lst = recnames.get(name) */ ++ lst = PyDict_GetItem(recnames, name); ++ ++ /* ++ if lst: ++ lst.append(rec) ++ else: ++ recnames[name] = [rec] ++ */ ++ if (lst) { ++ PyList_Append(lst, rec); ++ } else { ++ lst = PyList_New(1); ++ Py_INCREF(rec); ++ PyList_SET_ITEM(lst, 0, rec); ++ PyDict_SetItem(recnames, name, lst); ++ Py_DECREF(lst); ++ } ++ } ++ ++ Py_DECREF(names); ++ Py_DECREF(seq); ++ } ++ + /* upgnames = {} */ + upgnames = PyDict_New(); + /* for upg in self._upgrades: */ +@@ -3035,6 +3236,56 @@ Cache_linkDeps(CacheObject *self, PyObject *args) + } + } + ++ /* lst = recnames.get(prv.name) */ ++ lst = PyDict_GetItem(recnames, prv->name); ++ ++ /* if lst: */ ++ if (lst) { ++ /* for rec in lst: */ ++ int reclen = PyList_GET_SIZE(lst); ++ for (j = 0; j != reclen; j++) { ++ DependsObject *rec = (DependsObject *)PyList_GET_ITEM(lst, j); ++ /* if rec.matches(prv): */ ++ PyObject *ret = PyObject_CallMethod((PyObject *)rec, "matches", ++ "O", (PyObject *)prv); ++ if (!ret) return NULL; ++ if (PyObject_IsTrue(ret)) { ++ /* ++ if rec.providedby: ++ rec.providedby.append(prv) ++ else: ++ rec.providedby = [prv] ++ */ ++ if (PyList_Check(rec->providedby)) { ++ PyList_Append(rec->providedby, (PyObject *)prv); ++ } else { ++ PyObject *_lst = PyList_New(1); ++ Py_INCREF(prv); ++ PyList_SET_ITEM(_lst, 0, (PyObject *)prv); ++ Py_DECREF(rec->providedby); ++ rec->providedby = _lst; ++ } ++ ++ /* ++ if prv.recommendedby: ++ prv.recommendedby.append(prv) ++ else: ++ prv.recommendedby = [prv] ++ */ ++ if (PyList_Check(prv->recommendedby)) { ++ PyList_Append(prv->recommendedby, (PyObject *)rec); ++ } else { ++ PyObject *_lst = PyList_New(1); ++ Py_INCREF(rec); ++ PyList_SET_ITEM(_lst, 0, (PyObject *)rec); ++ Py_DECREF(prv->recommendedby); ++ prv->recommendedby = _lst; ++ } ++ } ++ Py_DECREF(ret); ++ } ++ } ++ + /* lst = upgnames.get(prv.name) */ + lst = PyDict_GetItem(upgnames, prv->name); + +@@ -3139,6 +3390,7 @@ Cache_linkDeps(CacheObject *self, PyObject *args) + } + + Py_DECREF(reqnames); ++ Py_DECREF(recnames); + Py_DECREF(upgnames); + Py_DECREF(cnfnames); + +@@ -3215,6 +3467,29 @@ Cache_getRequires(CacheObject *self, PyObject *args) + } + + PyObject * ++Cache_getRecommends(CacheObject *self, PyObject *args) ++{ ++ const char *name = NULL; ++ PyObject *lst; ++ int i, len; ++ if (!PyArg_ParseTuple(args, "|s", &name)) ++ return NULL; ++ if (!name) { ++ Py_INCREF(self->_recommends); ++ return self->_recommends; ++ } ++ lst = PyList_New(0); ++ len = PyList_GET_SIZE(self->_recommends); ++ for (i = 0; i != len; i++) { ++ DependsObject *rec = ++ (DependsObject*)PyList_GET_ITEM(self->_recommends, i); ++ if (strcmp(STR(rec->name), name) == 0) ++ PyList_Append(lst, (PyObject *)rec); ++ } ++ return lst; ++} ++ ++PyObject * + Cache_getUpgrades(CacheObject *self, PyObject *args) + { + const char *name = NULL; +@@ -3324,6 +3599,38 @@ Cache_search(CacheObject *self, PyObject *searcher) + } + Py_DECREF(lst); + ++ lst = PyObject_GetAttrString(searcher, "recommends"); ++ if (lst == NULL || !PyList_Check(lst)) { ++ PyErr_SetString(PyExc_TypeError, "Invalid recommends attribute"); ++ return NULL; ++ } ++ for (i = 0; i != PyList_GET_SIZE(lst); i++) { ++ ProvidesObject *prv = (ProvidesObject *)PyList_GET_ITEM(lst, i); ++ for (j = 0; j != PyList_GET_SIZE(self->_recommends); j++) { ++ PyObject *rec = PyList_GET_ITEM(self->_recommends, j); ++ PyObject *names = PyObject_CallMethod(rec, "getMatchNames", NULL); ++ PyObject *seq = PySequence_Fast(names, "getMatchNames() returned " ++ "non-sequence object"); ++ if (seq == NULL) return NULL; ++ for (k = 0; k != PySequence_Fast_GET_SIZE(seq); k++) { ++ if (strcmp(PyString_AS_STRING(PySequence_Fast_GET_ITEM(seq, k)), ++ PyString_AS_STRING(prv->name)) == 0) { ++ res = PyObject_CallMethod(rec, "matches", "O", prv); ++ if (res == NULL) ++ return NULL; ++ if (PyObject_IsTrue(res)) ++ CALLMETHOD(searcher, "addResult", "O", rec); ++ Py_DECREF(res); ++ break; ++ } ++ } ++ ++ Py_DECREF(names); ++ Py_DECREF(seq); ++ } ++ } ++ Py_DECREF(lst); ++ + lst = PyObject_GetAttrString(searcher, "upgrades"); + if (lst == NULL || !PyList_Check(lst)) { + PyErr_SetString(PyExc_TypeError, "Invalid upgrades attribute"); +@@ -3420,7 +3727,7 @@ Cache__getstate__(CacheObject *self, PyObject *args) + static PyObject * + Cache__setstate__(CacheObject *self, PyObject *state) + { +- PyObject *provides, *requires, *upgrades, *conflicts; ++ PyObject *provides, *requires, *recommends, *upgrades, *conflicts; + int i, ilen; + int j, jlen; + +@@ -3452,11 +3759,13 @@ Cache__setstate__(CacheObject *self, PyObject *state) + /* + provides = {} + requires = {} ++ recommends = {} + upgrades = {} + conflicts = {} + */ + provides = PyDict_New(); + requires = PyDict_New(); ++ recommends = PyDict_New(); + upgrades = PyDict_New(); + conflicts = PyDict_New(); + +@@ -3497,6 +3806,21 @@ Cache__setstate__(CacheObject *self, PyObject *state) + } + + /* ++ for rec in pkg.recommends: ++ rec.packages.append(pkg) ++ recommends[rec] = True ++ */ ++ if (PyList_Check(pkgobj->recommends)) { ++ jlen = PyList_GET_SIZE(pkgobj->recommends); ++ for (j = 0; j != jlen; j++) { ++ PyObject *rec = PyList_GET_ITEM(pkgobj->recommends, j); ++ DependsObject *recobj = (DependsObject *)rec; ++ PyList_Append(recobj->packages, pkg); ++ PyDict_SetItem(recommends, rec, Py_True); ++ } ++ } ++ ++ /* + for upg in pkg.upgrades: + upg.packages.append(pkg) + upgrades[upg] = True +@@ -3525,6 +3849,7 @@ Cache__setstate__(CacheObject *self, PyObject *state) + PyDict_SetItem(conflicts, cnf, Py_True); + } + } ++ + } + + /* self._provides = provides.keys() */ +@@ -3535,6 +3860,10 @@ Cache__setstate__(CacheObject *self, PyObject *state) + self->_requires = PyDict_Keys(requires); + Py_DECREF(requires); + ++ /* self._recommends = recommends.keys() */ ++ self->_recommends = PyDict_Keys(recommends); ++ Py_DECREF(recommends); ++ + /* self._upgrades = upgrades.keys() */ + self->_upgrades = PyDict_Keys(upgrades); + Py_DECREF(upgrades); +@@ -3562,6 +3891,7 @@ static PyMethodDef Cache_methods[] = { + {"getPackages", (PyCFunction)Cache_getPackages, METH_VARARGS, NULL}, + {"getProvides", (PyCFunction)Cache_getProvides, METH_VARARGS, NULL}, + {"getRequires", (PyCFunction)Cache_getRequires, METH_VARARGS, NULL}, ++ {"getRecommends", (PyCFunction)Cache_getRecommends, METH_VARARGS, NULL}, + {"getUpgrades", (PyCFunction)Cache_getUpgrades, METH_VARARGS, NULL}, + {"getConflicts", (PyCFunction)Cache_getConflicts, METH_VARARGS, NULL}, + {"search", (PyCFunction)Cache_search, METH_O, NULL}, +@@ -3576,6 +3906,7 @@ static PyMemberDef Cache_members[] = { + {"_packages", T_OBJECT, OFF(_packages), RO, 0}, + {"_provides", T_OBJECT, OFF(_provides), RO, 0}, + {"_requires", T_OBJECT, OFF(_requires), RO, 0}, ++ {"_recommends", T_OBJECT, OFF(_recommends), RO, 0}, + {"_upgrades", T_OBJECT, OFF(_upgrades), RO, 0}, + {"_conflicts", T_OBJECT, OFF(_conflicts), RO, 0}, + {"_objmap", T_OBJECT, OFF(_objmap), RO, 0}, +diff --git a/smart/commands/query.py b/smart/commands/query.py +index 808e53a..9265cd9 100644 +--- a/smart/commands/query.py ++++ b/smart/commands/query.py +@@ -107,6 +107,8 @@ def option_parser(**kwargs): + help=_("show requires for the given packages")) + parser.add_option("--show-prerequires", action="store_true", + help=_("show requires selecting only pre-dependencies")) ++ parser.add_option("--show-recommends", action="store_true", ++ help=_("show recommends for the given packages")) + parser.add_option("--show-upgrades", action="store_true", + help=_("show upgrades for the given packages")) + parser.add_option("--show-conflicts", action="store_true", +@@ -488,6 +490,19 @@ def main(ctrl, opts, reloadchannels=True): + continue + output.showRequiresProvidedBy(pkg, req, + prv, prvpkg) ++ if pkg.recommends and (opts.show_recommends): ++ pkg.recommends.sort() ++ first = True ++ for req in pkg.recommends: ++ output.showRecommends(pkg, req) ++ if opts.show_providedby and req.providedby: ++ for prv in req.providedby: ++ prv.packages.sort() ++ for prvpkg in prv.packages: ++ if opts.installed and not prvpkg.installed: ++ continue ++ output.showRecommendsProvidedBy(pkg, req, ++ prv, prvpkg) + if pkg.upgrades and (opts.show_upgrades or whoupgrades): + pkg.upgrades.sort() + first = True +@@ -594,6 +609,12 @@ class NullOutput(object): + def showRequiresProvidedBy(self, pkg, req, prv, prvpkg): + pass + ++ def showRecommends(self, pkg, req): ++ pass ++ ++ def showRecommendsProvidedBy(self, pkg, req, prv, prvpkg): ++ pass ++ + def showUpgrades(self, pkg, upg): + pass + +@@ -619,6 +640,8 @@ class TextOutput(NullOutput): + self._firstconflictedby = True + self._firstrequires = True + self._firstrequiresprovidedby = True ++ self._firstrecommends = True ++ self._firstrecommendsprovidedby = True + self._firstupgrades = True + self._firstupgradesprovidedby = True + self._firstconflicts = True +@@ -711,6 +734,22 @@ class TextOutput(NullOutput): + name = str(prvpkg) + print " ", "%s (%s)" % (name, prv) + ++ def showRecommends(self, pkg, rec): ++ if self._firstrecommends: ++ self._firstrecommends = False ++ print " ", _("Recommends:") ++ print " ", rec ++ ++ def showRecommendsProvidedBy(self, pkg, req, prv, prvpkg): ++ if self._firstrecommendsprovidedby: ++ self._firstrecommendsprovidedby = False ++ print " ", _("Provided By:") ++ if self.opts.hide_version: ++ name = prvpkg.name ++ else: ++ name = str(prvpkg) ++ print " ", "%s (%s)" % (name, prv) ++ + def showUpgrades(self, pkg, upg): + if self._firstupgrades: + self._firstupgrades = False +@@ -797,6 +836,18 @@ class GraphVizOutput(NullOutput): + self._shown[req, prv] = True + print ' "Requires: %s" -> "Provides: %s";' % (req, prv) + ++ def showRecommends(self, pkg, req): ++ if (pkg, req) not in self._shown: ++ self._shown[pkg, req] = True ++ print ' "%s" -> "Recommends: %s";' % (pkg, req) ++ ++ def showRecommendsProvidedBy(self, pkg, req, prv, prvpkg): ++ self.showPackage(prvpkg) ++ self.showProvides(prvpkg, prv) ++ if (req, prv) not in self._shown: ++ self._shown[req, prv] = True ++ print ' "Recommends: %s" -> "Provides: %s";' % (req, prv) ++ + def showUpgrades(self, pkg, upg): + if (pkg, upg) not in self._shown: + self._shown[pkg, upg] = True +diff --git a/smart/control.py b/smart/control.py +index fd7083a..d44abe7 100644 +--- a/smart/control.py ++++ b/smart/control.py +@@ -447,7 +447,7 @@ class Control(object): + queue = marked.keys() + while queue: + pkg = queue.pop(0) +- for req in pkg.requires: ++ for req in pkg.requires + pkg.recommends: + for prv in req.providedby: + for prvpkg in prv.packages: + if (prvpkg.installed and +@@ -794,7 +794,7 @@ class Control(object): + pkglst = [] + for pkg in changeset: + n = 0 +- for req in pkg.requires: ++ for req in pkg.requires + pkg.recommends: + for prv in req.providedby: + for prvpkg in prv.packages: + if changeset.get(prvpkg) is INSTALL: +diff --git a/smart/searcher.py b/smart/searcher.py +index 216f4ce..32eb825 100644 +--- a/smart/searcher.py ++++ b/smart/searcher.py +@@ -45,9 +45,9 @@ class Searcher(object): + + - provides is matched in Provides.search(), for the same reason. + +- - requires, upgrades, and conflicts don't have special searching +- methods. Instead, their usual match() method is given an instance +- of the Provides type. ++ - requires, recommends, upgrades, and conflicts don't have special ++ searching methods. Instead, their usual match() method is given ++ an instance of the Provides type. + + - group, path, url, and other information which is found by + PackageInfo, is searched by the Loader.search() method and +@@ -62,6 +62,7 @@ class Searcher(object): + self.nameversion = [] + self.provides = [] + self.requires = [] ++ self.recommends = [] + self.upgrades = [] + self.conflicts = [] + self.path = [] +@@ -76,6 +77,7 @@ class Searcher(object): + del self.nameversion[:] + del self.provides[:] + del self.requires[:] ++ del self.recommends[:] + del self.upgrades[:] + del self.conflicts[:] + del self.path[:] +@@ -122,6 +124,8 @@ class Searcher(object): + self.addProvides(s[9:], cutoff) + elif s.startswith("requires:"): + self.addRequires(s[9:]) ++ elif s.startswith("recommends:"): ++ self.addRecommends(s[11:]) + elif s.startswith("upgrades:"): + self.addUpgrades(s[9:]) + elif s.startswith("conflicts:"): +@@ -151,6 +155,7 @@ class Searcher(object): + return s and ( + s.startswith("provides:") or + s.startswith("requires:") or ++ s.startswith("recommends:") or + s.startswith("upgrades:") or + s.startswith("conflicts:") or + s.startswith("url:") or +@@ -182,6 +187,9 @@ class Searcher(object): + def addRequires(self, s): + self.requires.append(self._buildProvides(s)) + ++ def addRecommends(self, s): ++ self.recommends.append(self._buildProvides(s)) ++ + def addUpgrades(self, s): + self.upgrades.append(self._buildProvides(s)) + +diff --git a/smart/transaction.py b/smart/transaction.py +index eb320d2..300b9cc 100644 +--- a/smart/transaction.py ++++ b/smart/transaction.py +@@ -573,7 +573,7 @@ class Transaction(object): + self._remove(namepkg, changeset, locked, pending, depth) + + # Install packages required by this one. +- for req in pkg.requires: ++ for req in pkg.requires + pkg.recommends: + + # Check if someone is already providing it. + prvpkgs = {} +@@ -596,8 +596,12 @@ class Transaction(object): + + if not prvpkgs: + # No packages provide it at all. Give up. +- raise Failed, _("Can't install %s: no package provides %s") % \ +- (pkg, req) ++ if req in pkg.requires: ++ raise Failed, _("Can't install %s: no package provides %s") % \ ++ (pkg, req) ++ else: ++ # It's only a recommend, skip ++ continue + + if len(prvpkgs) == 1: + # Don't check locked here. prvpkgs was +@@ -1359,7 +1363,7 @@ class ChangeSetSplitter(object): + set = self._changeset + + # Check all dependencies needed by this package. +- for req in pkg.requires: ++ for req in pkg.requires + pkg.recommends: + + # Check if any already installed or to be installed + # package will solve the problem. +@@ -1424,8 +1428,9 @@ class ChangeSetSplitter(object): + + # There are no solutions for the problem. + # Should we really care about it? +- if (self._forcerequires or +- isinstance(req, PreRequires)): ++ if ((self._forcerequires or ++ isinstance(req, PreRequires)) ++ and req in pkg.requires): + raise Error, _("No providers for '%s', " + "required by '%s'") % (req, pkg) + +@@ -1625,7 +1630,7 @@ def recursiveInternalRequires(pkgmap, pkg, numrel, done=None): + return n + + def forwardRequires(pkg, map): +- for req in pkg.requires: ++ for req in pkg.requires + pkg.recommends: + if req not in map: + map[req] = True + for prv in req.providedby: +@@ -1794,6 +1799,15 @@ def checkPackages(cache, checkset, relateset, report=False): + iface.info(_("Unsatisfied dependency: %s requires %s") % + (pkg, req)) + ++ for req in pkg.recommends: ++ for prv in req.providedby: ++ for prvpkg in prv.packages: ++ if prvpkg in relateset: ++ break ++ else: ++ continue ++ break ++ + if not pkg.installed: + continue + +-- +1.7.9.5 + -- cgit v1.2.3-54-g00ecf