summaryrefslogtreecommitdiffstats
path: root/meta/recipes-devtools/python/python-smartpm
diff options
context:
space:
mode:
Diffstat (limited to 'meta/recipes-devtools/python/python-smartpm')
-rw-r--r--meta/recipes-devtools/python/python-smartpm/smart-already-installed-message.patch54
-rw-r--r--meta/recipes-devtools/python/python-smartpm/smart-attempt.patch223
-rw-r--r--meta/recipes-devtools/python/python-smartpm/smart-channelsdir.patch24
-rw-r--r--meta/recipes-devtools/python/python-smartpm/smart-config-ignore-all-recommends.patch24
-rw-r--r--meta/recipes-devtools/python/python-smartpm/smart-conflict-provider.patch196
-rw-r--r--meta/recipes-devtools/python/python-smartpm/smart-dflags.patch45
-rw-r--r--meta/recipes-devtools/python/python-smartpm/smart-filename-NAME_MAX.patch35
-rw-r--r--meta/recipes-devtools/python/python-smartpm/smart-flag-exclude-packages.patch70
-rw-r--r--meta/recipes-devtools/python/python-smartpm/smart-flag-ignore-recommends.patch60
-rw-r--r--meta/recipes-devtools/python/python-smartpm/smart-improve-error-reporting.patch253
-rw-r--r--meta/recipes-devtools/python/python-smartpm/smart-metadata-match.patch28
-rw-r--r--meta/recipes-devtools/python/python-smartpm/smart-multilib-fixes.patch22
-rw-r--r--meta/recipes-devtools/python/python-smartpm/smart-recommends.patch1362
-rw-r--r--meta/recipes-devtools/python/python-smartpm/smart-rpm-extra-macros.patch27
-rw-r--r--meta/recipes-devtools/python/python-smartpm/smart-rpm-md-parse.patch26
-rw-r--r--meta/recipes-devtools/python/python-smartpm/smart-rpm-root.patch80
-rw-r--r--meta/recipes-devtools/python/python-smartpm/smart-rpm4-fixes.patch49
-rw-r--r--meta/recipes-devtools/python/python-smartpm/smart-tmpdir.patch30
-rw-r--r--meta/recipes-devtools/python/python-smartpm/smart-yaml-error.patch86
-rw-r--r--meta/recipes-devtools/python/python-smartpm/smartpm-rpm5-nodig.patch46
20 files changed, 2740 insertions, 0 deletions
diff --git a/meta/recipes-devtools/python/python-smartpm/smart-already-installed-message.patch b/meta/recipes-devtools/python/python-smartpm/smart-already-installed-message.patch
new file mode 100644
index 0000000000..e264de8a58
--- /dev/null
+++ b/meta/recipes-devtools/python/python-smartpm/smart-already-installed-message.patch
@@ -0,0 +1,54 @@
1
2From a74a9a9eb9d75964a0e978950e8b191d7a18d763 Mon Sep 17 00:00:00 2001
3From: Paul Eggleton <paul.eggleton@linux.intel.com>
4Date: Fri, 5 Jun 2015 17:07:16 +0100
5Subject: [PATCH] smart: change "is already installed" message from warning to
6 info
7
8This doesn't need to be a warning.
9
10Upstream-Status: Pending
11
12Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
13---
14 smart/commands/install.py | 4 ++--
15 smart/interfaces/text/interactive.py | 2 +-
16 2 files changed, 3 insertions(+), 3 deletions(-)
17
18diff --git a/smart/commands/install.py b/smart/commands/install.py
19index 6ef9682..80d456b 100644
20--- a/smart/commands/install.py
21+++ b/smart/commands/install.py
22@@ -152,7 +152,7 @@ def main(ctrl, opts):
23 for obj in results:
24 for pkg in obj.packages:
25 if pkg.installed:
26- iface.warning(_("%s (for %s) is already installed")
27+ iface.info(_("%s (for %s) is already installed")
28 % (pkg, arg))
29 installed = True
30 break
31@@ -184,7 +184,7 @@ def main(ctrl, opts):
32 for name in names:
33 pkg = names[name][0]
34 if pkg.installed:
35- iface.warning(_("%s is already installed") % pkg)
36+ iface.info(_("%s is already installed") % pkg)
37 else:
38 trans.enqueue(pkg, INSTALL)
39
40diff --git a/smart/interfaces/text/interactive.py b/smart/interfaces/text/interactive.py
41index 9865584..190867b 100644
42--- a/smart/interfaces/text/interactive.py
43+++ b/smart/interfaces/text/interactive.py
44@@ -278,7 +278,7 @@ class Interpreter(Cmd):
45 for name in names:
46 pkg = names[name][0]
47 if pkg.installed:
48- iface.warning(_("%s is already installed") % pkg)
49+ iface.info(_("%s is already installed") % pkg)
50 else:
51 found = True
52 transaction.enqueue(pkg, INSTALL)
53--
542.1.0
diff --git a/meta/recipes-devtools/python/python-smartpm/smart-attempt.patch b/meta/recipes-devtools/python/python-smartpm/smart-attempt.patch
new file mode 100644
index 0000000000..82d2e6cf31
--- /dev/null
+++ b/meta/recipes-devtools/python/python-smartpm/smart-attempt.patch
@@ -0,0 +1,223 @@
1From b105e7fe812da3ccaf7155c0fe14c8728b0d39a5 Mon Sep 17 00:00:00 2001
2From: Mark Hatle <mark.hatle@windriver.com>
3Date: Mon, 20 Jan 2014 14:30:52 +0000
4Subject: [PATCH] Add mechanism to attempt install without failing
5
6In OpenEmbedded, for complementary and 'attemptonly' package processing,
7we need a way to instruct smart to try to install, but ignore any
8failures (usually conflicts).
9
10This option only works for the install operation.
11
12If a complementary install fails, an actual error occurred, one that
13we can't ignore without losing the entire attempted transaction. Keep
14this as an error so that we can catch these cases in the futre.
15
16Upstream-Status: Pending
17
18Signed-off-by: Mark Hatle <mark.hatle@windriver.com>
19Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
20---
21 smart.py | 5 +++-
22 smart/commands/install.py | 5 ++++
23 smart/transaction.py | 65 +++++++++++++++++++++++++++++++++++------------
24 3 files changed, 58 insertions(+), 17 deletions(-)
25
26Index: smart-1.4.1/smart/commands/install.py
27===================================================================
28--- smart-1.4.1.orig/smart/commands/install.py
29+++ smart-1.4.1/smart/commands/install.py
30@@ -50,6 +50,8 @@ def option_parser():
31 parser = OptionParser(usage=USAGE,
32 description=DESCRIPTION,
33 examples=EXAMPLES)
34+ parser.add_option("--attempt", action="store_true",
35+ help=_("attempt to install packages, ignore failures"))
36 parser.add_option("--stepped", action="store_true",
37 help=_("split operation in steps"))
38 parser.add_option("--urls", action="store_true",
39@@ -80,6 +82,9 @@ def main(ctrl, opts):
40 if not opts.args:
41 raise Error, _("no package(s) given")
42
43+ if opts.attempt:
44+ sysconf.set("attempt-install", True, soft=True)
45+
46 if opts.explain:
47 sysconf.set("explain-changesets", True, soft=True)
48
49Index: smart-1.4.1/smart/transaction.py
50===================================================================
51--- smart-1.4.1.orig/smart/transaction.py
52+++ smart-1.4.1/smart/transaction.py
53@@ -555,6 +555,8 @@ class Transaction(object):
54 changeset.set(pkg, INSTALL)
55 isinst = changeset.installed
56
57+ attempt = sysconf.has("attempt-install", soft=True)
58+
59 # Remove packages conflicted by this one.
60 for cnf in pkg.conflicts:
61 for prv in cnf.providedby:
62@@ -564,11 +566,16 @@ class Transaction(object):
63 if not isinst(prvpkg):
64 locked[prvpkg] = (LOCKED_CONFLICT_BY, pkg)
65 continue
66- if prvpkg in locked:
67- raise Failed, _("Can't install %s: conflicted package "
68- "%s is locked") % (pkg, prvpkg)
69- self._remove(prvpkg, changeset, locked, pending, depth)
70- pending.append((PENDING_UPDOWN, prvpkg))
71+ if attempt:
72+ del changeset[pkg]
73+ raise Failed, _("Can't install %s: it conflicts with package "
74+ "%s") % (pkg, prvpkg)
75+ else:
76+ if prvpkg in locked:
77+ raise Failed, _("Can't install %s: conflicted package "
78+ "%s is locked") % (pkg, prvpkg)
79+ self._remove(prvpkg, changeset, locked, pending, depth)
80+ pending.append((PENDING_UPDOWN, prvpkg))
81
82 # Remove packages conflicting with this one.
83 for prv in pkg.provides:
84@@ -579,12 +586,18 @@ class Transaction(object):
85 if not isinst(cnfpkg):
86 locked[cnfpkg] = (LOCKED_CONFLICT, pkg)
87 continue
88- if cnfpkg in locked:
89+ if attempt:
90+ del changeset[pkg]
91 raise Failed, _("Can't install %s: it's conflicted by "
92- "the locked package %s") \
93- % (pkg, cnfpkg)
94- self._remove(cnfpkg, changeset, locked, pending, depth)
95- pending.append((PENDING_UPDOWN, cnfpkg))
96+ "the package %s") \
97+ % (pkg, cnfpkg)
98+ else:
99+ if cnfpkg in locked:
100+ raise Failed, _("Can't install %s: it's conflicted by "
101+ "the locked package %s") \
102+ % (pkg, cnfpkg)
103+ self._remove(cnfpkg, changeset, locked, pending, depth)
104+ pending.append((PENDING_UPDOWN, cnfpkg))
105
106 # Remove packages with the same name that can't
107 # coexist with this one.
108@@ -594,10 +607,15 @@ class Transaction(object):
109 if not isinst(namepkg):
110 locked[namepkg] = (LOCKED_NO_COEXIST, pkg)
111 continue
112- if namepkg in locked:
113+ if attempt:
114+ del changeset[pkg]
115 raise Failed, _("Can't install %s: it can't coexist "
116 "with %s") % (pkg, namepkg)
117- self._remove(namepkg, changeset, locked, pending, depth)
118+ else:
119+ if namepkg in locked:
120+ raise Failed, _("Can't install %s: it can't coexist "
121+ "with %s") % (pkg, namepkg)
122+ self._remove(namepkg, changeset, locked, pending, depth)
123
124 # Install packages required by this one.
125 for req in pkg.requires + pkg.recommends:
126@@ -1176,6 +1194,8 @@ class Transaction(object):
127
128 self._policy.runStarting()
129
130+ attempt = sysconf.has("attempt-install", soft=True)
131+
132 try:
133 changeset = self._changeset.copy()
134 isinst = changeset.installed
135@@ -1190,7 +1210,11 @@ class Transaction(object):
136 locked[pkg] = (LOCKED_KEEP, None)
137 elif op is INSTALL:
138 if not isinst(pkg) and pkg in locked:
139- raise Failed, _("Can't install %s: it's locked") % pkg
140+ if attempt:
141+ iface.warning(_("Can't install %s: it's locked") % pkg)
142+ del changeset[pkg]
143+ else:
144+ raise Failed, _("Can't install %s: it's locked") % pkg
145 changeset.set(pkg, INSTALL)
146 locked[pkg] = (LOCKED_INSTALL, None)
147 elif op is REMOVE:
148@@ -1216,9 +1240,18 @@ class Transaction(object):
149 else:
150 op = REMOVE
151 if op is INSTALL or op is REINSTALL:
152- self._install(pkg, changeset, locked, pending)
153- if pkg in changeset:
154- changeset.setRequested(pkg, True)
155+ try:
156+ self._install(pkg, changeset, locked, pending)
157+ if pkg in changeset:
158+ changeset.setRequested(pkg, True)
159+ except Failed, e:
160+ if attempt:
161+ iface.warning(_("Can't install %s: %s") % (pkg, e))
162+ if pkg in changeset:
163+ del changeset[pkg]
164+ continue
165+ else:
166+ raise Failed, e
167 elif op is REMOVE:
168 self._remove(pkg, changeset, locked, pending)
169 elif op is UPGRADE:
170Index: smart-1.4.1/smart/backends/rpm/pm.py
171===================================================================
172--- smart-1.4.1.orig/smart/backends/rpm/pm.py
173+++ smart-1.4.1/smart/backends/rpm/pm.py
174@@ -243,15 +253,48 @@ class RPMPackageManager(PackageManager):
175 cb = RPMCallback(prog, upgradednames)
176 cb.grabOutput(True)
177 probs = None
178+ retry = 0
179 try:
180 probs = ts.run(cb, None)
181 finally:
182 del getTS.ts
183 cb.grabOutput(False)
184+ if probs and sysconf.has("attempt-install", soft=True):
185+ def remove_conflict(pkgNEVR):
186+ for key in changeset.keys():
187+ if pkgNEVR == str(key):
188+ del changeset[key]
189+ del pkgpaths[key]
190+ iface.warning("Removing %s due to file %s conflicting with %s" % (pkgNEVR, fname, altNEVR))
191+ break
192+
193+ retry = 1
194+ for prob in probs:
195+ if prob[1][0] == rpm.RPMPROB_NEW_FILE_CONFLICT:
196+ msg = prob[0].split()
197+ fname = msg[1]
198+ pkgNEVR = msg[7]
199+ altNEVR = msg[9]
200+ pkgNEVR = pkgNEVR.rsplit('.', 1)[0] + '@' + pkgNEVR.rsplit('.', 1)[1]
201+ altNEVR = altNEVR.rsplit('.', 1)[0] + '@' + altNEVR.rsplit('.', 1)[1]
202+ remove_conflict(pkgNEVR)
203+ elif prob[1][0] == rpm.RPMPROB_FILE_CONFLICT:
204+ msg = prob[0].split()
205+ fname = msg[1]
206+ pkgNEVR = msg[5]
207+ altNEVR = msg[11]
208+ pkgNEVR = pkgNEVR.rsplit('.', 1)[0] + '@' + pkgNEVR.rsplit('.', 1)[1]
209+ altNEVR = altNEVR.rsplit('.', 1)[0] + '@' + altNEVR.rsplit('.', 1)[1]
210+ remove_conflict(pkgNEVR)
211+ else:
212+ retry = 0
213+
214 prog.setDone()
215- if probs:
216+ if probs and (not retry):
217 raise Error, "\n".join([x[0] for x in probs])
218 prog.stop()
219+ if retry and len(changeset):
220+ self.commit(changeset, pkgpaths)
221
222 class RPMCallback:
223 def __init__(self, prog, upgradednames):
diff --git a/meta/recipes-devtools/python/python-smartpm/smart-channelsdir.patch b/meta/recipes-devtools/python/python-smartpm/smart-channelsdir.patch
new file mode 100644
index 0000000000..e621b33875
--- /dev/null
+++ b/meta/recipes-devtools/python/python-smartpm/smart-channelsdir.patch
@@ -0,0 +1,24 @@
1Make CHANNELSDIR in smart empty, since this causes host contamination issues
2on some RPM-based hosts on which smart is already installed.
3
4[YOCTO #3881]
5
6Upstream-Status: Inappropriate [embedded specific]
7
8diff --git a/smart/plugins/channelsync.py b/smart/plugins/channelsync.py
9index 3ba95ff..646d696 100644
10--- a/smart/plugins/channelsync.py
11+++ b/smart/plugins/channelsync.py
12@@ -23,7 +23,11 @@ from smart.channel import *
13 from smart import *
14 import os
15
16-CHANNELSDIR = "/etc/smart/channels/"
17+# For now, we leave the definition of CHANNELSDIR empty. This prevents smart
18+# from erroneously consider the build host's channels while setting up its
19+# channels [YOCTO #3881]. If this feature will be used in the future, CHANNELSDIR
20+# should be set to a proper value.
21+CHANNELSDIR = ""
22
23 def syncChannels(channelsdir, force=None):
24
diff --git a/meta/recipes-devtools/python/python-smartpm/smart-config-ignore-all-recommends.patch b/meta/recipes-devtools/python/python-smartpm/smart-config-ignore-all-recommends.patch
new file mode 100644
index 0000000000..df9d7799e8
--- /dev/null
+++ b/meta/recipes-devtools/python/python-smartpm/smart-config-ignore-all-recommends.patch
@@ -0,0 +1,24 @@
1Add a simple method to disable the install of recommended packages
2
3Upstream-Status: Pending
4
5Usage:
6 smart config --set ignore-all-recommends=1
7
8Signed-off-by: Mark Hatle <mark.hatle@windriver.com>
9
10Index: smart-1.4.1/smart/transaction.py
11===================================================================
12--- smart-1.4.1.orig/smart/transaction.py
13+++ smart-1.4.1/smart/transaction.py
14@@ -611,7 +611,9 @@ class Transaction(object):
15 for prv in req.providedby:
16 for prvpkg in prv.packages:
17 if not reqrequired:
18- if pkgconf.testFlag("ignore-recommends", prvpkg):
19+ if sysconf.get("ignore-all-recommends", 0) == 1:
20+ continue
21+ elif pkgconf.testFlag("ignore-recommends", prvpkg):
22 continue
23 if isinst(prvpkg):
24 found = True
diff --git a/meta/recipes-devtools/python/python-smartpm/smart-conflict-provider.patch b/meta/recipes-devtools/python/python-smartpm/smart-conflict-provider.patch
new file mode 100644
index 0000000000..10a7447cb4
--- /dev/null
+++ b/meta/recipes-devtools/python/python-smartpm/smart-conflict-provider.patch
@@ -0,0 +1,196 @@
1Report a reason when a dependency could not be installed because it is locked
2
3If a requirement of a package is conflicted, depending on how the
4solution is reached, the transaction code may eliminate all providers
5of the requirement and then error out because nothing provides them. To
6work around this, store a reason in the locked dict and report that back
7if we need to, so for example instead of:
8
9 error: Can't install packagegroup-core-ssh-dropbear-1.0-r1@all: no package provides dropbear
10
11we now get:
12
13 error: Can't install packagegroup-core-ssh-dropbear-1.0-r1@all: unable to install provider for dropbear:
14 error: dropbear-2013.58-r1.0@armv5te is conflicted by openssh-sshd-6.2p2-r0@armv5te
15
16Upstream-Status: Pending
17
18Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
19---
20 smart/const.py | 7 +++++++
21 smart/transaction.py | 58 +++++++++++++++++++++++++++++++++++++++++-----------
22 2 files changed, 53 insertions(+), 12 deletions(-)
23
24diff --git a/smart/const.py b/smart/const.py
25index 4d8e5cb..67c1ac5 100644
26--- a/smart/const.py
27+++ b/smart/const.py
28@@ -70,4 +70,11 @@ DATADIR = "/var/lib/smart/"
29 USERDATADIR = "~/.smart/"
30 CONFFILE = "config"
31
32+LOCKED_INSTALL = Enum('LOCKED_INSTALL')
33+LOCKED_REMOVE = Enum('LOCKED_REMOVE')
34+LOCKED_CONFLICT = Enum('LOCKED_CONFLICT')
35+LOCKED_CONFLICT_BY = Enum('LOCKED_CONFLICT_BY')
36+LOCKED_NO_COEXIST = Enum('LOCKED_NO_COEXIST')
37+LOCKED_SYSCONF = Enum('LOCKED_SYSCONF')
38+
39 # vim:ts=4:sw=4:et
40diff --git a/smart/transaction.py b/smart/transaction.py
41index 300b9cc..dd9aa38 100644
42--- a/smart/transaction.py
43+++ b/smart/transaction.py
44@@ -19,10 +19,31 @@
45 # along with Smart Package Manager; if not, write to the Free Software
46 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
47 #
48-from smart.const import INSTALL, REMOVE, UPGRADE, FIX, REINSTALL, KEEP
49+from smart.const import INSTALL, REMOVE, UPGRADE, FIX, REINSTALL, KEEP, LOCKED_INSTALL, LOCKED_CONFLICT, LOCKED_CONFLICT_BY, LOCKED_NO_COEXIST, LOCKED_SYSCONF, LOCKED_REMOVE
50 from smart.cache import PreRequires, Package
51 from smart import *
52
53+def lock_reason(pkg, lockvalue):
54+ try:
55+ (reason, otherpkg) = lockvalue
56+ except TypeError:
57+ reason = None
58+ lockvalue = None
59+ if reason == LOCKED_INSTALL:
60+ return _("%s is to be installed") % pkg
61+ elif reason == LOCKED_CONFLICT:
62+ return _("%s conflicts with %s") % (pkg, otherpkg)
63+ elif reason == LOCKED_CONFLICT_BY:
64+ return _("%s is conflicted by %s") % (pkg, otherpkg)
65+ elif reason == LOCKED_NO_COEXIST:
66+ return _("%s cannot coexist with %s") % (pkg, otherpkg)
67+ elif reason == LOCKED_SYSCONF:
68+ return _("%s is locked in system configuration") % pkg
69+ elif reason == LOCKED_REMOVE:
70+ return _("%s is to be removed") % pkg
71+ else:
72+ return _("%s is locked (unknown reason)") % pkg
73+
74 class ChangeSet(dict):
75
76 def __init__(self, cache, state=None, requested=None):
77@@ -187,7 +208,7 @@ class Policy(object):
78 for pkg in pkgconf.filterByFlag("lock", cache.getPackages()):
79 if pkg not in self._locked:
80 self._sysconflocked.append(pkg)
81- self._locked[pkg] = True
82+ self._locked[pkg] = (LOCKED_SYSCONF, None)
83
84 def runFinished(self):
85 self._priorities.clear()
86@@ -524,7 +545,7 @@ class Transaction(object):
87 if ownpending:
88 pending = []
89
90- locked[pkg] = True
91+ locked[pkg] = (LOCKED_INSTALL, None)
92 changeset.set(pkg, INSTALL)
93 isinst = changeset.installed
94
95@@ -535,7 +556,7 @@ class Transaction(object):
96 if prvpkg is pkg:
97 continue
98 if not isinst(prvpkg):
99- locked[prvpkg] = True
100+ locked[prvpkg] = (LOCKED_CONFLICT_BY, pkg)
101 continue
102 if prvpkg in locked:
103 raise Failed, _("Can't install %s: conflicted package "
104@@ -550,7 +571,7 @@ class Transaction(object):
105 if cnfpkg is pkg:
106 continue
107 if not isinst(cnfpkg):
108- locked[cnfpkg] = True
109+ locked[cnfpkg] = (LOCKED_CONFLICT, pkg)
110 continue
111 if cnfpkg in locked:
112 raise Failed, _("Can't install %s: it's conflicted by "
113@@ -565,7 +586,7 @@ class Transaction(object):
114 for namepkg in namepkgs:
115 if namepkg is not pkg and not pkg.coexists(namepkg):
116 if not isinst(namepkg):
117- locked[namepkg] = True
118+ locked[namepkg] = (LOCKED_NO_COEXIST, pkg)
119 continue
120 if namepkg in locked:
121 raise Failed, _("Can't install %s: it can't coexist "
122@@ -577,6 +598,7 @@ class Transaction(object):
123
124 # Check if someone is already providing it.
125 prvpkgs = {}
126+ lockedpkgs = {}
127 found = False
128 for prv in req.providedby:
129 for prvpkg in prv.packages:
130@@ -585,6 +607,8 @@ class Transaction(object):
131 break
132 if prvpkg not in locked:
133 prvpkgs[prvpkg] = True
134+ else:
135+ lockedpkgs[prvpkg] = locked[prvpkg]
136 else:
137 continue
138 break
139@@ -597,7 +621,17 @@ class Transaction(object):
140 if not prvpkgs:
141 # No packages provide it at all. Give up.
142 if req in pkg.requires:
143- raise Failed, _("Can't install %s: no package provides %s") % \
144+ reasons = []
145+ for prv in req.providedby:
146+ for prvpkg in prv.packages:
147+ lockedres = lockedpkgs.get(prvpkg, None)
148+ if lockedres:
149+ reasons.append(lock_reason(prvpkg, lockedres))
150+ if reasons:
151+ raise Failed, _("Can't install %s: unable to install provider for %s:\n %s") % \
152+ (pkg, req, '\n '.join(reasons))
153+ else:
154+ raise Failed, _("Can't install %s: no package provides %s") % \
155 (pkg, req)
156 else:
157 # It's only a recommend, skip
158@@ -627,7 +661,7 @@ class Transaction(object):
159 if ownpending:
160 pending = []
161
162- locked[pkg] = True
163+ locked[pkg] = (LOCKED_REMOVE, None)
164 changeset.set(pkg, REMOVE)
165 isinst = changeset.installed
166
167@@ -1140,22 +1174,22 @@ class Transaction(object):
168 if op is KEEP:
169 if pkg in changeset:
170 del changeset[pkg]
171- locked[pkg] = True
172+ locked[pkg] = (LOCKED_KEEP, None)
173 elif op is INSTALL:
174 if not isinst(pkg) and pkg in locked:
175 raise Failed, _("Can't install %s: it's locked") % pkg
176 changeset.set(pkg, INSTALL)
177- locked[pkg] = True
178+ locked[pkg] = (LOCKED_INSTALL, None)
179 elif op is REMOVE:
180 if isinst(pkg) and pkg in locked:
181 raise Failed, _("Can't remove %s: it's locked") % pkg
182 changeset.set(pkg, REMOVE)
183- locked[pkg] = True
184+ locked[pkg] = (LOCKED_REMOVE, None)
185 elif op is REINSTALL:
186 if pkg in locked:
187 raise Failed, _("Can't reinstall %s: it's locked")%pkg
188 changeset.set(pkg, INSTALL, force=True)
189- locked[pkg] = True
190+ locked[pkg] = (LOCKED_INSTALL, None)
191 elif op is UPGRADE:
192 pass
193
194--
1951.8.1.2
196
diff --git a/meta/recipes-devtools/python/python-smartpm/smart-dflags.patch b/meta/recipes-devtools/python/python-smartpm/smart-dflags.patch
new file mode 100644
index 0000000000..531ea51cad
--- /dev/null
+++ b/meta/recipes-devtools/python/python-smartpm/smart-dflags.patch
@@ -0,0 +1,45 @@
1backends/rpm: add support for setting dependency flags
2
3This is useful for OpenEmbedded so that we can do the equivalent of
4the --nolinktos and --noparentdirs rpm command line options.
5
6Upstream-Status: Pending
7
8Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
9
10[sgw - Added try/catch for rpm4 since it does not have setDFlags() API]
11
12Signed-off-by: Saul Wold <sgw@linux.intel.com>
13
14
15Index: smart-1.4.1/smart/backends/rpm/pm.py
16===================================================================
17--- smart-1.4.1.orig/smart/backends/rpm/pm.py
18+++ smart-1.4.1/smart/backends/rpm/pm.py
19@@ -106,6 +106,26 @@ class RPMPackageManager(PackageManager):
20 flags |= rpm.RPMTRANS_FLAG_TEST
21 ts.setFlags(flags)
22
23+ try:
24+ dflags = ts.setDFlags(0)
25+ if sysconf.get("rpm-noupgrade", False):
26+ dflags |= rpm.RPMDEPS_FLAG_NOUPGRADE
27+ if sysconf.get("rpm-norequires", False):
28+ dflags |= rpm.RPMDEPS_FLAG_NOREQUIRES
29+ if sysconf.get("rpm-noconflicts", False):
30+ dflags |= rpm.RPMDEPS_FLAG_NOCONFLICTS
31+ if sysconf.get("rpm-noobsoletes", False):
32+ dflags |= rpm.RPMDEPS_FLAG_NOOBSOLETES
33+ if sysconf.get("rpm-noparentdirs", False):
34+ dflags |= rpm.RPMDEPS_FLAG_NOPARENTDIRS
35+ if sysconf.get("rpm-nolinktos", False):
36+ dflags |= rpm.RPMDEPS_FLAG_NOLINKTOS
37+ if sysconf.get("rpm-nosuggest", False):
38+ dflags |= rpm.RPMDEPS_FLAG_NOSUGGEST
39+ ts.setDFlags(dflags)
40+ except AttributeError, ae:
41+ pass
42+
43 # Set rpm verbosity level.
44 levelname = sysconf.get('rpm-log-level')
45 level = {
diff --git a/meta/recipes-devtools/python/python-smartpm/smart-filename-NAME_MAX.patch b/meta/recipes-devtools/python/python-smartpm/smart-filename-NAME_MAX.patch
new file mode 100644
index 0000000000..22794a7388
--- /dev/null
+++ b/meta/recipes-devtools/python/python-smartpm/smart-filename-NAME_MAX.patch
@@ -0,0 +1,35 @@
1From a17998b6be3319ae476a64f366737bc267a53a8a Mon Sep 17 00:00:00 2001
2From: Robert Yang <liezhi.yang@windriver.com>
3Date: Mon, 16 Sep 2013 05:54:13 -0400
4Subject: [PATCH] fetcher.py: truncate the filename to meet NAME_MAX
5
6The function getLocalPath() converts the filepath into the filename,
7there would be a "File name too long" error when len(filename) >
8NAME_MAX, truncate it to meet NAME_MAX will fix the problem.
9
10Signed-off-by: Robert Yang <liezhi.yang@windriver.com>
11---
12 smart/fetcher.py | 8 ++++++++
13 1 file changed, 8 insertions(+)
14
15diff --git a/smart/fetcher.py b/smart/fetcher.py
16--- a/smart/fetcher.py
17+++ b/smart/fetcher.py
18@@ -139,6 +139,14 @@ class Fetcher(object):
19 filename = os.path.basename(path)
20 if self._localpathprefix:
21 filename = self._localpathprefix+filename
22+ # pathconf requires the path existed
23+ if not os.path.exists(self._localdir):
24+ os.makedirs(self._localdir)
25+ name_max = os.pathconf(self._localdir, 'PC_NAME_MAX')
26+ # The length of the filename should be less than NAME_MAX
27+ if len(filename) > name_max:
28+ iface.debug(_("Truncate %s to %s") % (filename, filename[-name_max:]))
29+ filename = filename[-name_max:]
30 return os.path.join(self._localdir, filename)
31
32 def setForceCopy(self, value):
33--
341.7.10.4
35
diff --git a/meta/recipes-devtools/python/python-smartpm/smart-flag-exclude-packages.patch b/meta/recipes-devtools/python/python-smartpm/smart-flag-exclude-packages.patch
new file mode 100644
index 0000000000..21a28746a1
--- /dev/null
+++ b/meta/recipes-devtools/python/python-smartpm/smart-flag-exclude-packages.patch
@@ -0,0 +1,70 @@
1Add exclude-packages flag support
2
3Allow configuring specific packages to be excluded. This will allow
4users to specify things NOT to install, and if they are attempted an
5error will be generated.
6
7Upstream-Status: Pending
8
9Signed-off-by: Mark Hatle <mark.hatle@windriver.com>
10
11Index: smart-1.4.1/smart/const.py
12===================================================================
13--- smart-1.4.1.orig/smart/const.py
14+++ smart-1.4.1/smart/const.py
15@@ -70,6 +70,7 @@ DATADIR = "/var/lib/smart/"
16 USERDATADIR = "~/.smart/"
17 CONFFILE = "config"
18
19+LOCKED_EXCLUDE = Enum('LOCKED_EXCLUDE')
20 LOCKED_INSTALL = Enum('LOCKED_INSTALL')
21 LOCKED_REMOVE = Enum('LOCKED_REMOVE')
22 LOCKED_CONFLICT = Enum('LOCKED_CONFLICT')
23Index: smart-1.4.1/smart/transaction.py
24===================================================================
25--- smart-1.4.1.orig/smart/transaction.py
26+++ smart-1.4.1/smart/transaction.py
27@@ -19,7 +19,7 @@
28 # along with Smart Package Manager; if not, write to the Free Software
29 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30 #
31-from smart.const import INSTALL, REMOVE, UPGRADE, FIX, REINSTALL, KEEP, LOCKED_INSTALL, LOCKED_CONFLICT, LOCKED_CONFLICT_BY, LOCKED_NO_COEXIST, LOCKED_SYSCONF, LOCKED_REMOVE
32+from smart.const import INSTALL, REMOVE, UPGRADE, FIX, REINSTALL, KEEP, LOCKED_EXCLUDE, LOCKED_INSTALL, LOCKED_CONFLICT, LOCKED_CONFLICT_BY, LOCKED_NO_COEXIST, LOCKED_SYSCONF, LOCKED_REMOVE
33 from smart.cache import PreRequires, Package
34 from smart import *
35
36@@ -29,7 +29,9 @@ def lock_reason(pkg, lockvalue):
37 except TypeError:
38 reason = None
39 lockvalue = None
40- if reason == LOCKED_INSTALL:
41+ if reason == LOCKED_EXCLUDE:
42+ return _("%s is to be excluded") % pkg
43+ elif reason == LOCKED_INSTALL:
44 return _("%s is to be installed") % pkg
45 elif reason == LOCKED_CONFLICT:
46 return _("%s conflicts with %s") % (pkg, otherpkg)
47@@ -210,6 +212,10 @@ class Policy(object):
48 self._sysconflocked.append(pkg)
49 self._locked[pkg] = (LOCKED_SYSCONF, None)
50
51+ for pkg in pkgconf.filterByFlag("exclude-packages", cache.getPackages()):
52+ if pkg not in self._locked:
53+ self._locked[pkg] = (LOCKED_EXCLUDE, None)
54+
55 def runFinished(self):
56 self._priorities.clear()
57 for pkg in self._sysconflocked:
58Index: smart-1.4.1/smart/commands/flag.py
59===================================================================
60--- smart-1.4.1.orig/smart/commands/flag.py
61+++ smart-1.4.1/smart/commands/flag.py
62@@ -47,6 +47,8 @@ Currently known flags are:
63 multi-version - Flagged packages may have more than one version
64 installed in the system at the same time
65 (backend dependent).
66+ exclude-packages - Flagged packages will be excluded, if they are
67+ required, an error will be generated.
68 ignore-recommends - Flagged packages will not be installed, if
69 they are only recommended by a package to be
70 installed rather than required.
diff --git a/meta/recipes-devtools/python/python-smartpm/smart-flag-ignore-recommends.patch b/meta/recipes-devtools/python/python-smartpm/smart-flag-ignore-recommends.patch
new file mode 100644
index 0000000000..5d5c6f4346
--- /dev/null
+++ b/meta/recipes-devtools/python/python-smartpm/smart-flag-ignore-recommends.patch
@@ -0,0 +1,60 @@
1Add ignore-recommends flag support
2
3Allow configuring recommends on specific packages to be ignored.
4
5Upstream-Status: Pending
6
7Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
8---
9 smart/commands/flag.py | 3 +++
10 smart/transaction.py | 7 ++++++-
11 2 files changed, 9 insertions(+), 1 deletion(-)
12
13diff --git a/smart/commands/flag.py b/smart/commands/flag.py
14index 8b90496..191bb11 100644
15--- a/smart/commands/flag.py
16+++ b/smart/commands/flag.py
17@@ -47,6 +47,9 @@ Currently known flags are:
18 multi-version - Flagged packages may have more than one version
19 installed in the system at the same time
20 (backend dependent).
21+ ignore-recommends - Flagged packages will not be installed, if
22+ they are only recommended by a package to be
23+ installed rather than required.
24
25 security - Flagged packages are updates for security errata.
26 bugfix - Flagged packages are updates for bugfix errata.
27diff --git a/smart/transaction.py b/smart/transaction.py
28index dd9aa38..38eabae 100644
29--- a/smart/transaction.py
30+++ b/smart/transaction.py
31@@ -596,12 +596,17 @@ class Transaction(object):
32 # Install packages required by this one.
33 for req in pkg.requires + pkg.recommends:
34
35+ reqrequired = req in pkg.requires
36+
37 # Check if someone is already providing it.
38 prvpkgs = {}
39 lockedpkgs = {}
40 found = False
41 for prv in req.providedby:
42 for prvpkg in prv.packages:
43+ if not reqrequired:
44+ if pkgconf.testFlag("ignore-recommends", prvpkg):
45+ continue
46 if isinst(prvpkg):
47 found = True
48 break
49@@ -620,7 +625,7 @@ class Transaction(object):
50
51 if not prvpkgs:
52 # No packages provide it at all. Give up.
53- if req in pkg.requires:
54+ if reqrequired:
55 reasons = []
56 for prv in req.providedby:
57 for prvpkg in prv.packages:
58--
591.8.1.2
60
diff --git a/meta/recipes-devtools/python/python-smartpm/smart-improve-error-reporting.patch b/meta/recipes-devtools/python/python-smartpm/smart-improve-error-reporting.patch
new file mode 100644
index 0000000000..2ca0f6d593
--- /dev/null
+++ b/meta/recipes-devtools/python/python-smartpm/smart-improve-error-reporting.patch
@@ -0,0 +1,253 @@
1Improve error reporting in smart
2
3Add code to check proper command line arguments for various
4smart commands. Exit with error if erroneous/additional arguments
5are given in the command line.
6
7Upstream-Status: Pending
8
9Signed-off-by: Bogdan Marinescu <bogdan.a.marinescu@intel.com>
10
11diff --git a/smart/commands/channel.py b/smart/commands/channel.py
12index aa76f91..63fbb35 100644
13--- a/smart/commands/channel.py
14+++ b/smart/commands/channel.py
15@@ -157,7 +157,17 @@ def main(ctrl, opts):
16 opts.show is None and opts.yaml is None):
17 iface.warning(_("Can't edit channels information."))
18 raise Error, _("Configuration is in readonly mode.")
19-
20+
21+ # Argument check
22+ opts.check_args_of_option("set", -1)
23+ opts.check_args_of_option("remove", -1)
24+ opts.check_args_of_option("edit", 0)
25+ opts.check_args_of_option("enable", -1)
26+ opts.check_args_of_option("disable", -1)
27+ opts.ensure_action("channel", ["add", "set", "remove", "remove_all",
28+ "list", "show", "yaml", "enable", "disable"])
29+ opts.check_remaining_args()
30+
31 if opts.add is not None:
32 if not opts.add and opts.args == ["-"]:
33 newchannels = []
34diff --git a/smart/commands/check.py b/smart/commands/check.py
35index b08608a..506e852 100644
36--- a/smart/commands/check.py
37+++ b/smart/commands/check.py
38@@ -72,6 +72,9 @@ def parse_options(argv):
39
40 def main(ctrl, opts, reloadchannels=True):
41
42+ # Argument check
43+ opts.check_args_of_option("channels", 1)
44+
45 if sysconf.get("auto-update"):
46 from smart.commands import update
47 updateopts = update.parse_options([])
48diff --git a/smart/commands/config.py b/smart/commands/config.py
49index dd50dee..4fe4366 100644
50--- a/smart/commands/config.py
51+++ b/smart/commands/config.py
52@@ -80,6 +80,12 @@ def main(ctrl, opts):
53 globals["false"] = False
54 globals["no"] = False
55
56+ # Check arguments
57+ opts.check_args_of_option("set", -1)
58+ opts.check_args_of_option("remove", -1)
59+ opts.ensure_action("config", ["set", "show", "yaml", "remove"])
60+ opts.check_remaining_args()
61+
62 if opts.set:
63 for opt in opts.set:
64 m = SETRE.match(opt)
65diff --git a/smart/commands/download.py b/smart/commands/download.py
66index 6837993..b853c61 100644
67--- a/smart/commands/download.py
68+++ b/smart/commands/download.py
69@@ -81,6 +81,14 @@ def parse_options(argv):
70
71 def main(ctrl, opts):
72
73+ # Argument check
74+ opts.check_args_of_option("target", 1)
75+ opts.check_args_of_option("output", 1)
76+ opts.check_args_of_option("from_urls", -1)
77+ opts.check_args_of_option("from_metalink", -1)
78+ if not opts.args and not opts.from_metalink and not opts.from_urls:
79+ raise Error, _("no package(s) given")
80+
81 packages = []
82 if opts.args:
83 if sysconf.get("auto-update"):
84diff --git a/smart/commands/info.py b/smart/commands/info.py
85index 12f74f0..59fbe98 100644
86--- a/smart/commands/info.py
87+++ b/smart/commands/info.py
88@@ -58,6 +58,10 @@ def parse_options(argv):
89
90 def main(ctrl, opts, reloadchannels=True):
91
92+ # Argument check
93+ if not opts.args:
94+ raise Error, _("No package(s) given")
95+
96 if sysconf.get("auto-update"):
97 from smart.commands import update
98 updateopts = update.parse_options([])
99diff --git a/smart/commands/install.py b/smart/commands/install.py
100index 8a45954..590222c 100644
101--- a/smart/commands/install.py
102+++ b/smart/commands/install.py
103@@ -76,6 +76,10 @@ def parse_options(argv):
104
105 def main(ctrl, opts):
106
107+ # Argument check
108+ if not opts.args:
109+ raise Error, _("no package(s) given")
110+
111 if opts.explain:
112 sysconf.set("explain-changesets", True, soft=True)
113
114diff --git a/smart/commands/reinstall.py b/smart/commands/reinstall.py
115index e59d896..32da3e6 100644
116--- a/smart/commands/reinstall.py
117+++ b/smart/commands/reinstall.py
118@@ -68,7 +68,11 @@ def parse_options(argv):
119 return opts
120
121 def main(ctrl, opts):
122-
123+
124+ # Argument check
125+ if not opts.args:
126+ raise Error, _("no package(s) given")
127+
128 if opts.explain:
129 sysconf.set("explain-changesets", True, soft=True)
130
131diff --git a/smart/commands/remove.py b/smart/commands/remove.py
132index b4823a6..acd3bbd 100644
133--- a/smart/commands/remove.py
134+++ b/smart/commands/remove.py
135@@ -74,6 +74,10 @@ def parse_options(argv):
136
137 def main(ctrl, opts):
138
139+ # Argument check
140+ if not opts.args:
141+ raise Error, _("no package(s) given")
142+
143 if opts.explain:
144 sysconf.set("explain-changesets", True, soft=True)
145
146diff --git a/smart/commands/search.py b/smart/commands/search.py
147index 0d0b573..44806b8 100644
148--- a/smart/commands/search.py
149+++ b/smart/commands/search.py
150@@ -44,6 +44,8 @@ def option_parser():
151 def parse_options(argv):
152 opts = query.parse_options(argv, usage=USAGE, \
153 description=DESCRIPTION, examples=EXAMPLES)
154+ if not argv:
155+ raise Error, _("Search expression not specified")
156 opts.name = opts.args
157 opts.summary = opts.args
158 opts.description = opts.args
159diff --git a/smart/commands/upgrade.py b/smart/commands/upgrade.py
160index ec86290..7e290d8 100644
161--- a/smart/commands/upgrade.py
162+++ b/smart/commands/upgrade.py
163@@ -91,6 +91,9 @@ def parse_options(argv):
164
165 def main(ctrl, opts):
166
167+ # Argument check
168+ opts.check_args_of_option("flag", 1)
169+
170 if opts.explain:
171 sysconf.set("explain-changesets", True, soft=True)
172
173diff --git a/smart/util/optparse.py b/smart/util/optparse.py
174index 4a3d3a8..279b0bf 100644
175--- a/smart/util/optparse.py
176+++ b/smart/util/optparse.py
177@@ -70,6 +70,8 @@ import sys, os
178 import types
179 import textwrap
180 from gettext import gettext as _
181+from smart import Error
182+import re
183
184 def _repr(self):
185 return "<%s at 0x%x: %s>" % (self.__class__.__name__, id(self), self)
186@@ -708,6 +710,12 @@ class Option:
187 self.action, self.dest, opt, value, values, parser)
188
189 def take_action(self, action, dest, opt, value, values, parser):
190+ # Keep all the options in the command line in the '_given_opts' array
191+ # This will be used later to validate the command line
192+ given_opts = getattr(parser.values, "_given_opts", [])
193+ user_opt = re.sub(r"^\-*", "", opt).replace("-", "_")
194+ given_opts.append(user_opt)
195+ setattr(parser.values, "_given_opts", given_opts)
196 if action == "store":
197 setattr(values, dest, value)
198 elif action == "store_const":
199@@ -819,6 +827,54 @@ class Values:
200 setattr(self, attr, value)
201 return getattr(self, attr)
202
203+ # Check if the given option has the specified number of arguments
204+ # Raise an error if the option has an invalid number of arguments
205+ # A negative number for 'nargs' means "at least |nargs| arguments are needed"
206+ def check_args_of_option(self, opt, nargs, err=None):
207+ given_opts = getattr(self, "_given_opts", [])
208+ if not opt in given_opts:
209+ return
210+ values = getattr(self, opt, [])
211+ if type(values) != type([]):
212+ return
213+ if nargs < 0:
214+ nargs = -nargs
215+ if len(values) >= nargs:
216+ return
217+ if not err:
218+ if nargs == 1:
219+ err = _("Option '%s' requires at least one argument") % opt
220+ else:
221+ err = _("Option '%s' requires at least %d arguments") % (opt, nargs)
222+ raise Error, err
223+ elif nargs == 0:
224+ if len( values ) == 0:
225+ return
226+ raise Error, err
227+ else:
228+ if len(values) == nargs:
229+ return
230+ if not err:
231+ if nargs == 1:
232+ err = _("Option '%s' requires one argument") % opt
233+ else:
234+ err = _("Option '%s' requires %d arguments") % (opt, nargs)
235+ raise Error, err
236+
237+ # Check that at least one of the options in 'actlist' was given as an argument
238+ # to the command 'cmdname'
239+ def ensure_action(self, cmdname, actlist):
240+ given_opts = getattr(self, "_given_opts", [])
241+ for action in actlist:
242+ if action in given_opts:
243+ return
244+ raise Error, _("No action specified for command '%s'") % cmdname
245+
246+ # Check if there are any other arguments left after parsing the command line and
247+ # raise an error if such arguments are found
248+ def check_remaining_args(self):
249+ if self.args:
250+ raise Error, _("Invalid argument(s) '%s'" % str(self.args))
251
252 class OptionContainer:
253
diff --git a/meta/recipes-devtools/python/python-smartpm/smart-metadata-match.patch b/meta/recipes-devtools/python/python-smartpm/smart-metadata-match.patch
new file mode 100644
index 0000000000..d06f416605
--- /dev/null
+++ b/meta/recipes-devtools/python/python-smartpm/smart-metadata-match.patch
@@ -0,0 +1,28 @@
1smart - backends/rmp/metadata.py: Fix incorrect call to the match function
2
3The match function should take three parameters, name, comparison, version...
4The original code was passing it a reference to the object holding the data
5instead, which caused the comparison in match to always fail.
6
7Upstream-Status: Pending
8
9Signed-off-by: Mark Hatle <mark.hatle@windriver.com>
10
11--- a/smart/backends/rpm/metadata.py
12+++ b/smart/backends/rpm/metadata.py
13@@ -332,13 +332,13 @@
14 reqargs = [x for x in reqdict
15 if not ((x[2] is None or "=" in x[2]) and
16 (RPMProvides, x[1], x[3]) in prvdict or
17- system_provides.match(*x[:3]))]
18+ system_provides.match(x[1], x[2], x[3]))]
19 reqargs = collapse_libc_requires(reqargs)
20
21 recargs = [x for x in recdict
22 if not ((x[2] is None or "=" in x[2]) and
23 (RPMProvides, x[1], x[3]) in prvdict or
24- system_provides.match(*x[:3]))]
25+ system_provides.match(x[1], x[2], x[3]))]
26
27 prvargs = prvdict.keys()
28 cnfargs = cnfdict.keys()
diff --git a/meta/recipes-devtools/python/python-smartpm/smart-multilib-fixes.patch b/meta/recipes-devtools/python/python-smartpm/smart-multilib-fixes.patch
new file mode 100644
index 0000000000..56fef79a5f
--- /dev/null
+++ b/meta/recipes-devtools/python/python-smartpm/smart-multilib-fixes.patch
@@ -0,0 +1,22 @@
1To fix some multilib issues, change the way the RPM backend decides
2if two packages can coexist: if they have a different architecture,
3automatically assume that they can coexist (which is fundamental for
4multilib).
5
6Upstream-Status: Pending
7
8Signed-off-by: Bogdan Marinescu <bogdan.a.marinescu@intel.com>
9
10diff --git a/smart/backends/rpm/base.py b/smart/backends/rpm/base.py
11index 6e83d40..7140c1b 100644
12--- a/smart/backends/rpm/base.py
13+++ b/smart/backends/rpm/base.py
14@@ -228,6 +228,8 @@ class RPMPackage(Package):
15 return False
16 selfver, selfarch = splitarch(self.version)
17 otherver, otherarch = splitarch(other.version)
18+ if selfarch != otherarch:
19+ return True
20 selfcolor = getArchColor(selfarch)
21 othercolor = getArchColor(otherarch)
22 if (selfcolor and othercolor and selfcolor != othercolor and
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/smart-rpm-extra-macros.patch b/meta/recipes-devtools/python/python-smartpm/smart-rpm-extra-macros.patch
new file mode 100644
index 0000000000..30cb1767dd
--- /dev/null
+++ b/meta/recipes-devtools/python/python-smartpm/smart-rpm-extra-macros.patch
@@ -0,0 +1,27 @@
1backends/rpm: implement rpm-extra-macros option
2
3Allow defining extra macros in the smart configuration to be passed
4to rpm before opening the database.
5
6Upstream-Status: Pending
7
8Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
9
10diff --git a/smart/backends/rpm/base.py b/smart/backends/rpm/base.py
11index b9e9cb2..234c844 100644
12--- a/smart/backends/rpm/base.py
13+++ b/smart/backends/rpm/base.py
14@@ -53,6 +53,10 @@ def rpm_join_dbpath(root, dbpath):
15 return os.path.join(root, dbpath)
16
17 def getTS(new=False):
18+ if sysconf.get("rpm-extra-macros"):
19+ for key, value in sysconf.get("rpm-extra-macros").items():
20+ rpm.addMacro(key, str(value))
21+
22 rpm_root = os.path.abspath(sysconf.get("rpm-root", "/"))
23 if not hasattr(getTS, "ts") or getTS.root != rpm_root:
24 getTS.root = rpm_root
25--
261.7.9.5
27
diff --git a/meta/recipes-devtools/python/python-smartpm/smart-rpm-md-parse.patch b/meta/recipes-devtools/python/python-smartpm/smart-rpm-md-parse.patch
new file mode 100644
index 0000000000..97cecc124d
--- /dev/null
+++ b/meta/recipes-devtools/python/python-smartpm/smart-rpm-md-parse.patch
@@ -0,0 +1,26 @@
1backends/rpm: fix parsing of rpm-md metadata
2
3If assertions are disabled then the queue.pop() wasn't being executed,
4leading to requires, recommends etc. not being read properly.
5
6Upstream-Status: Pending
7
8Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
9
10diff --git a/smart/backends/rpm/metadata.py b/smart/backends/rpm/metadata.py
11index 2c54f39..dc9df22 100644
12--- a/smart/backends/rpm/metadata.py
13+++ b/smart/backends/rpm/metadata.py
14@@ -188,7 +188,8 @@ class RPMMetaDataLoader(Loader):
15
16 elif event == "end":
17
18- assert queue.pop() is elem
19+ popped = queue.pop()
20+ assert popped is elem
21
22 if skip:
23 if tag == skip:
24--
251.7.9.5
26
diff --git a/meta/recipes-devtools/python/python-smartpm/smart-rpm-root.patch b/meta/recipes-devtools/python/python-smartpm/smart-rpm-root.patch
new file mode 100644
index 0000000000..b2629ef051
--- /dev/null
+++ b/meta/recipes-devtools/python/python-smartpm/smart-rpm-root.patch
@@ -0,0 +1,80 @@
1Fix smart RPM backend to handle rpm-dbpath/rpm-root properly
2
3Don't assume that if the dbpath starts with / that it is an absolute
4path. This matches the behaviour of rpm itself. (If the root path is
5specified and does not start with /, rpm will prepend the root path
6twice and fail).
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 7092332..0489e11 100644
14--- a/smart/backends/rpm/base.py
15+++ b/smart/backends/rpm/base.py
16@@ -46,6 +46,12 @@ __all__ = ["RPMPackage", "RPMProvides", "RPMNameProvides", "RPMPreRequires",
17 "rpm", "getTS", "getArchScore", "getArchColor", "system_provides",
18 "collapse_libc_requires"]
19
20+def rpm_join_dbpath(root, dbpath):
21+ if dbpath.startswith('/') and root:
22+ return os.path.join(root, dbpath[1:])
23+ else:
24+ return os.path.join(root, dbpath)
25+
26 def getTS(new=False):
27 rpm_root = os.path.abspath(sysconf.get("rpm-root", "/"))
28 if not hasattr(getTS, "ts") or getTS.root != rpm_root:
29@@ -56,7 +62,7 @@ def getTS(new=False):
30 #if not sysconf.get("rpm-check-signatures", False):
31 # getTS.ts.setVSFlags(rpm._RPMVSF_NOSIGNATURES)
32 rpm_dbpath = sysconf.get("rpm-dbpath", "var/lib/rpm")
33- dbdir = os.path.join(getTS.root, rpm_dbpath)
34+ dbdir = rpm_join_dbpath(getTS.root, rpm_dbpath)
35 if not os.path.isdir(dbdir):
36 try:
37 os.makedirs(dbdir)
38diff --git a/smart/channels/rpm_sys.py b/smart/channels/rpm_sys.py
39index efcb10e..b9fda27 100644
40--- a/smart/channels/rpm_sys.py
41+++ b/smart/channels/rpm_sys.py
42@@ -20,7 +20,7 @@
43 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
44 #
45 from smart.backends.rpm.header import RPMDBLoader
46-from smart.backends.rpm.base import getTS
47+from smart.backends.rpm.base import getTS, rpm_join_dbpath
48 from smart.channel import PackageChannel
49 from smart import *
50 import os
51@@ -32,9 +32,9 @@ class RPMSysChannel(PackageChannel):
52
53 def fetch(self, fetcher, progress):
54 getTS() # Make sure the db exists.
55- path = os.path.join(sysconf.get("rpm-root", "/"),
56- sysconf.get("rpm-dbpath", "var/lib/rpm"),
57- "Packages")
58+ dbdir = rpm_join_dbpath(sysconf.get("rpm-root", "/"),
59+ sysconf.get("rpm-dbpath", "var/lib/rpm"))
60+ path = os.path.join(dbdir, "Packages")
61 digest = os.path.getmtime(path)
62 if digest == self._digest:
63 return True
64diff --git a/smart/plugins/detectsys.py b/smart/plugins/detectsys.py
65index 2cd49ad..3959d07 100644
66--- a/smart/plugins/detectsys.py
67+++ b/smart/plugins/detectsys.py
68@@ -20,10 +20,11 @@
69 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
70 #
71 from smart import *
72+from smart.backends.rpm.base import rpm_join_dbpath
73 import os
74
75 def detectRPMSystem():
76- dir = os.path.join(sysconf.get("rpm-root", "/"),
77+ dir = rpm_join_dbpath(sysconf.get("rpm-root", "/"),
78 sysconf.get("rpm-dbpath", "var/lib/rpm"))
79 file = os.path.join(dir, "Packages")
80 if os.path.exists(file):
diff --git a/meta/recipes-devtools/python/python-smartpm/smart-rpm4-fixes.patch b/meta/recipes-devtools/python/python-smartpm/smart-rpm4-fixes.patch
new file mode 100644
index 0000000000..708ffe67d3
--- /dev/null
+++ b/meta/recipes-devtools/python/python-smartpm/smart-rpm4-fixes.patch
@@ -0,0 +1,49 @@
1
2This patch checks for rpm5 related functions in order to allow rpm4
3to work correctly. Currently the rpm4 archscore and filter work
4differently enough that they need to be changed.
5
6Upstream-Status: Inappropriate [OE-Core Specific]
7
8Signed-off-by: Saul Wold <sgw@linux.intel.com>
9
10Index: smart-1.4.1/smart/backends/rpm/base.py
11===================================================================
12--- smart-1.4.1.orig/smart/backends/rpm/base.py
13+++ smart-1.4.1/smart/backends/rpm/base.py
14@@ -338,10 +338,14 @@ class RPMObsoletes(Depends):
15
16 _SCOREMAP = {}
17 def getArchScore(arch, _sm=_SCOREMAP):
18- if arch not in _sm:
19- score = rpm.archscore(arch)
20- _sm[arch] = score
21- return _sm.get(arch, 0)
22+ try:
23+ rpm.platformscore(arch)
24+ if arch not in _sm:
25+ score = rpm.archscore(arch)
26+ _sm[arch] = score
27+ return _sm.get(arch, 0)
28+ except AttributeError:
29+ return 1
30
31 # TODO: Embed color into nameprovides and obsoletes relations.
32 _COLORMAP = {"noarch": 0, "x86_64": 2, "ppc64": 2, "s390x": 2, "sparc64": 2}
33Index: smart-1.4.1/smart/backends/rpm/pm.py
34===================================================================
35--- smart-1.4.1.orig/smart/backends/rpm/pm.py
36+++ smart-1.4.1/smart/backends/rpm/pm.py
37@@ -235,6 +235,12 @@ class RPMPackageManager(PackageManager):
38 if sysconf.get("rpm-order"):
39 ts.order()
40 probfilter = rpm.RPMPROB_FILTER_OLDPACKAGE
41+ try:
42+ # Test for RPM5 function
43+ rpm.platformscore("")
44+ except AttributeError:
45+ probfilter |= rpm.RPMPROB_FILTER_IGNOREARCH
46+
47 if force or reinstall:
48 probfilter |= rpm.RPMPROB_FILTER_REPLACEPKG
49 probfilter |= rpm.RPMPROB_FILTER_REPLACEOLDFILES
diff --git a/meta/recipes-devtools/python/python-smartpm/smart-tmpdir.patch b/meta/recipes-devtools/python/python-smartpm/smart-tmpdir.patch
new file mode 100644
index 0000000000..2f09ce9248
--- /dev/null
+++ b/meta/recipes-devtools/python/python-smartpm/smart-tmpdir.patch
@@ -0,0 +1,30 @@
1backends/rpm: remove creation of /var/tmp
2
3This doesn't appear to be needed, and breaks installation of base-files
4in OpenEmbedded (since that is a symlink installed as part of the
5package).
6
7Upstream-Status: Pending
8
9Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
10
11diff --git a/smart/backends/rpm/base.py b/smart/backends/rpm/base.py
12index 234c844..127354d 100644
13--- a/smart/backends/rpm/base.py
14+++ b/smart/backends/rpm/base.py
15@@ -82,12 +82,6 @@ def getTS(new=False):
16 else:
17 iface.warning(_("Initialized new rpm database at %s")
18 % getTS.root)
19- tmpdir = os.path.join(getTS.root, "var/tmp")
20- if not os.path.isdir(tmpdir):
21- try:
22- os.makedirs(tmpdir)
23- except OSError:
24- pass
25 if new:
26 if sysconf.get("rpm-dbpath"):
27 rpm.addMacro('_dbpath', "/" + sysconf.get("rpm-dbpath"))
28--
291.7.9.5
30
diff --git a/meta/recipes-devtools/python/python-smartpm/smart-yaml-error.patch b/meta/recipes-devtools/python/python-smartpm/smart-yaml-error.patch
new file mode 100644
index 0000000000..e16c5c12aa
--- /dev/null
+++ b/meta/recipes-devtools/python/python-smartpm/smart-yaml-error.patch
@@ -0,0 +1,86 @@
1Print a more friendly error if YAML output is requested without PyYAML
2
3Upstream-Status: Pending
4
5Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
6
7diff --git a/smart/commands/channel.py b/smart/commands/channel.py
8index 63fbb35..108f3f1 100644
9--- a/smart/commands/channel.py
10+++ b/smart/commands/channel.py
11@@ -339,7 +339,10 @@ def main(ctrl, opts):
12 print
13
14 if opts.yaml is not None:
15- import yaml
16+ try:
17+ import yaml
18+ except ImportError:
19+ raise Error, _("Please install PyYAML in order to use this function")
20 yamlchannels = {}
21 for alias in (opts.yaml or sysconf.get("channels", ())):
22 channel = sysconf.get(("channels", alias))
23diff --git a/smart/commands/config.py b/smart/commands/config.py
24index 4fe4366..aa1db78 100644
25--- a/smart/commands/config.py
26+++ b/smart/commands/config.py
27@@ -137,7 +137,10 @@ def main(ctrl, opts):
28 pprint.pprint(sysconf.get((), hard=True))
29
30 if opts.yaml is not None:
31- import yaml
32+ try:
33+ import yaml
34+ except ImportError:
35+ raise Error, _("Please install PyYAML in order to use this function")
36 if opts.yaml:
37 marker = object()
38 for opt in opts.yaml:
39diff --git a/smart/commands/flag.py b/smart/commands/flag.py
40index ed18999..8b90496 100644
41--- a/smart/commands/flag.py
42+++ b/smart/commands/flag.py
43@@ -138,7 +138,10 @@ def main(ctrl, opts):
44 print
45
46 if opts.yaml is not None:
47- import yaml
48+ try:
49+ import yaml
50+ except ImportError:
51+ raise Error, _("Please install PyYAML in order to use this function")
52 yamlflags = {}
53 for flag in opts.yaml or pkgconf.getFlagNames():
54 flag = flag.strip()
55diff --git a/smart/commands/mirror.py b/smart/commands/mirror.py
56index ca50a95..f7b019d 100644
57--- a/smart/commands/mirror.py
58+++ b/smart/commands/mirror.py
59@@ -218,7 +218,10 @@ def main(ctrl, opts):
60 print
61
62 if opts.yaml:
63- import yaml
64+ try:
65+ import yaml
66+ except ImportError:
67+ raise Error, _("Please install PyYAML in order to use this function")
68 yamlmirrors = {}
69 mirrors = sysconf.get("mirrors", ())
70 for origin in mirrors:
71diff --git a/smart/commands/priority.py b/smart/commands/priority.py
72index d850d29..441ea32 100644
73--- a/smart/commands/priority.py
74+++ b/smart/commands/priority.py
75@@ -117,7 +117,10 @@ def main(ctrl, opts):
76 print
77
78 elif opts.yaml:
79- import yaml
80+ try:
81+ import yaml
82+ except ImportError:
83+ raise Error, _("Please install PyYAML in order to use this function")
84 yamlpriorities = {}
85 priorities = sysconf.get("package-priorities", {})
86 for name in opts.args or priorities:
diff --git a/meta/recipes-devtools/python/python-smartpm/smartpm-rpm5-nodig.patch b/meta/recipes-devtools/python/python-smartpm/smartpm-rpm5-nodig.patch
new file mode 100644
index 0000000000..9919a941bc
--- /dev/null
+++ b/meta/recipes-devtools/python/python-smartpm/smartpm-rpm5-nodig.patch
@@ -0,0 +1,46 @@
1RPM5 has removed support for RPMVSF_NOSIGNATURES
2
3Patch smart to no longer use this flag
4
5Upstream-Status: Pending
6
7Signed-off-by: Mark Hatle <mark.hatle@windriver.com>
8
9diff -ur smart-1.4.1.orig/smart/backends/rpm/base.py smart-1.4.1/smart/backends/rpm/base.py
10--- smart-1.4.1.orig/smart/backends/rpm/base.py 2012-10-04 11:22:11.229351164 -0500
11+++ smart-1.4.1/smart/backends/rpm/base.py 2012-10-04 11:22:44.820170786 -0500
12@@ -53,8 +53,8 @@
13 if sysconf.get("rpm-dbpath"):
14 rpm.addMacro('_dbpath', "/" + sysconf.get("rpm-dbpath"))
15 getTS.ts = rpm.ts(getTS.root)
16- if not sysconf.get("rpm-check-signatures", False):
17- getTS.ts.setVSFlags(rpm._RPMVSF_NOSIGNATURES)
18+ #if not sysconf.get("rpm-check-signatures", False):
19+ # getTS.ts.setVSFlags(rpm._RPMVSF_NOSIGNATURES)
20 rpm_dbpath = sysconf.get("rpm-dbpath", "var/lib/rpm")
21 dbdir = os.path.join(getTS.root, rpm_dbpath)
22 if not os.path.isdir(dbdir):
23@@ -82,8 +82,8 @@
24 if sysconf.get("rpm-dbpath"):
25 rpm.addMacro('_dbpath', "/" + sysconf.get("rpm-dbpath"))
26 ts = rpm.ts(getTS.root)
27- if not sysconf.get("rpm-check-signatures", False):
28- ts.setVSFlags(rpm._RPMVSF_NOSIGNATURES)
29+ #if not sysconf.get("rpm-check-signatures", False):
30+ # ts.setVSFlags(rpm._RPMVSF_NOSIGNATURES)
31 return ts
32 else:
33 return getTS.ts
34diff -ur smart-1.4.1.orig/smart/plugins/yumchannelsync.py smart-1.4.1/smart/plugins/yumchannelsync.py
35--- smart-1.4.1.orig/smart/plugins/yumchannelsync.py 2010-12-06 03:11:05.000000000 -0600
36+++ smart-1.4.1/smart/plugins/yumchannelsync.py 2012-10-04 11:23:09.799350924 -0500
37@@ -56,7 +56,8 @@
38
39 rpmroot = sysconf.get("rpm-root", "/")
40 ts = rpmUtils.transaction.initReadOnlyTransaction(root=rpmroot)
41- ts.pushVSFlags(~(rpm._RPMVSF_NOSIGNATURES|rpm._RPMVSF_NODIGESTS))
42+ #ts.pushVSFlags(~(rpm._RPMVSF_NOSIGNATURES|rpm._RPMVSF_NODIGESTS))
43+ ts.pushVSFlags(~(rpm._RPMVSF_NODIGESTS))
44 releasever = None
45 # HACK: we're hard-coding the most used distros, will add more if needed
46 idx = ts.dbMatch('provides', 'fedora-release')