1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
|
# Copyright 1998-2012 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
__all__ = ["dbapi"]
import re
import portage
portage.proxy.lazyimport.lazyimport(globals(),
'portage.dbapi.dep_expand:dep_expand@_dep_expand',
'portage.dep:match_from_list',
'portage.output:colorize',
'portage.util:cmp_sort_key,writemsg',
'portage.versions:catsplit,catpkgsplit,vercmp,_pkg_str',
)
from portage import os
from portage import auxdbkeys
from portage.localization import _
class dbapi(object):
_category_re = re.compile(r'^\w[-.+\w]*$')
_categories = None
_use_mutable = False
_known_keys = frozenset(x for x in auxdbkeys
if not x.startswith("UNUSED_0"))
def __init__(self):
pass
@property
def categories(self):
"""
Use self.cp_all() to generate a category list. Mutable instances
can delete the self._categories attribute in cases when the cached
categories become invalid and need to be regenerated.
"""
if self._categories is not None:
return self._categories
self._categories = tuple(sorted(set(catsplit(x)[0] \
for x in self.cp_all())))
return self._categories
def close_caches(self):
pass
def cp_list(self, cp, use_cache=1):
raise NotImplementedError(self)
@staticmethod
def _cmp_cpv(cpv1, cpv2):
return vercmp(cpv1.version, cpv2.version)
@staticmethod
def _cpv_sort_ascending(cpv_list):
"""
Use this to sort self.cp_list() results in ascending
order. It sorts in place and returns None.
"""
if len(cpv_list) > 1:
# If the cpv includes explicit -r0, it has to be preserved
# for consistency in findname and aux_get calls, so use a
# dict to map strings back to their original values.
cpv_list.sort(key=cmp_sort_key(dbapi._cmp_cpv))
def cpv_all(self):
"""Return all CPVs in the db
Args:
None
Returns:
A list of Strings, 1 per CPV
This function relies on a subclass implementing cp_all, this is why the hasattr is there
"""
if not hasattr(self, "cp_all"):
raise NotImplementedError
cpv_list = []
for cp in self.cp_all():
cpv_list.extend(self.cp_list(cp))
return cpv_list
def cp_all(self):
""" Implement this in a child class
Args
None
Returns:
A list of strings 1 per CP in the datastore
"""
return NotImplementedError
def aux_get(self, mycpv, mylist, myrepo=None):
"""Return the metadata keys in mylist for mycpv
Args:
mycpv - "sys-apps/foo-1.0"
mylist - ["SLOT","DEPEND","HOMEPAGE"]
myrepo - The repository name.
Returns:
a list of results, in order of keys in mylist, such as:
["0",">=sys-libs/bar-1.0","http://www.foo.com"] or [] if mycpv not found'
"""
raise NotImplementedError
def aux_update(self, cpv, metadata_updates):
"""
Args:
cpv - "sys-apps/foo-1.0"
metadata_updates = { key : newvalue }
Returns:
None
"""
raise NotImplementedError
def match(self, origdep, use_cache=1):
"""Given a dependency, try to find packages that match
Args:
origdep - Depend atom
use_cache - Boolean indicating if we should use the cache or not
NOTE: Do we ever not want the cache?
Returns:
a list of packages that match origdep
"""
mydep = _dep_expand(origdep, mydb=self, settings=self.settings)
return list(self._iter_match(mydep,
self.cp_list(mydep.cp, use_cache=use_cache)))
def _iter_match(self, atom, cpv_iter):
cpv_iter = iter(match_from_list(atom, cpv_iter))
if atom.slot:
cpv_iter = self._iter_match_slot(atom, cpv_iter)
if atom.unevaluated_atom.use:
cpv_iter = self._iter_match_use(atom, cpv_iter)
if atom.repo:
cpv_iter = self._iter_match_repo(atom, cpv_iter)
return cpv_iter
def _iter_match_repo(self, atom, cpv_iter):
for cpv in cpv_iter:
try:
if self.aux_get(cpv, ["repository"], myrepo=atom.repo)[0] == atom.repo:
yield cpv
except KeyError:
continue
def _iter_match_slot(self, atom, cpv_iter):
for cpv in cpv_iter:
try:
if self.aux_get(cpv, ["SLOT"], myrepo=atom.repo)[0] == atom.slot:
yield cpv
except KeyError:
continue
def _iter_match_use(self, atom, cpv_iter):
"""
1) Check for required IUSE intersection (need implicit IUSE here).
2) Check enabled/disabled flag states.
"""
aux_keys = ["IUSE", "SLOT", "USE", "repository"]
for cpv in cpv_iter:
try:
metadata = dict(zip(aux_keys,
self.aux_get(cpv, aux_keys, myrepo=atom.repo)))
if not metadata["repository"]:
del metadata["repository"]
except KeyError:
continue
if not self._match_use(atom, cpv, metadata):
continue
yield cpv
def _match_use(self, atom, cpv, metadata):
iuse_implicit_match = self.settings._iuse_implicit_match
iuse = frozenset(x.lstrip('+-') for x in metadata["IUSE"].split())
for x in atom.unevaluated_atom.use.required:
if x not in iuse and not iuse_implicit_match(x):
return False
if atom.use is None:
pass
elif not self._use_mutable:
# Use IUSE to validate USE settings for built packages,
# in case the package manager that built this package
# failed to do that for some reason (or in case of
# data corruption).
use = frozenset(x for x in metadata["USE"].split()
if x in iuse or iuse_implicit_match(x))
missing_enabled = atom.use.missing_enabled.difference(iuse)
missing_disabled = atom.use.missing_disabled.difference(iuse)
if atom.use.enabled:
if atom.use.enabled.intersection(missing_disabled):
return False
need_enabled = atom.use.enabled.difference(use)
if need_enabled:
need_enabled = need_enabled.difference(missing_enabled)
if need_enabled:
return False
if atom.use.disabled:
if atom.use.disabled.intersection(missing_enabled):
return False
need_disabled = atom.use.disabled.intersection(use)
if need_disabled:
need_disabled = need_disabled.difference(missing_disabled)
if need_disabled:
return False
elif not self.settings.local_config:
# Check masked and forced flags for repoman.
if hasattr(cpv, 'slot'):
pkg = cpv
else:
pkg = _pkg_str(cpv, slot=metadata["SLOT"],
repo=metadata.get("repository"))
usemask = self.settings._getUseMask(pkg)
if usemask.intersection(atom.use.enabled):
return False
useforce = self.settings._getUseForce(pkg).difference(usemask)
if useforce.intersection(atom.use.disabled):
return False
return True
def invalidentry(self, mypath):
if '/-MERGING-' in mypath:
if os.path.exists(mypath):
writemsg(colorize("BAD", _("INCOMPLETE MERGE:"))+" %s\n" % mypath,
noiselevel=-1)
else:
writemsg("!!! Invalid db entry: %s\n" % mypath, noiselevel=-1)
def update_ents(self, updates, onProgress=None, onUpdate=None):
"""
Update metadata of all packages for package moves.
@param updates: A list of move commands, or dict of {repo_name: list}
@type updates: list or dict
@param onProgress: A progress callback function
@type onProgress: a callable that takes 2 integer arguments: maxval and curval
@param onUpdate: A progress callback function called only
for packages that are modified by updates.
@type onUpdate: a callable that takes 2 integer arguments:
maxval and curval
"""
cpv_all = self.cpv_all()
cpv_all.sort()
maxval = len(cpv_all)
aux_get = self.aux_get
aux_update = self.aux_update
meta_keys = ["DEPEND", "RDEPEND", "PDEPEND", "PROVIDE", 'repository']
repo_dict = None
if isinstance(updates, dict):
repo_dict = updates
from portage.update import update_dbentries
if onUpdate:
onUpdate(maxval, 0)
if onProgress:
onProgress(maxval, 0)
for i, cpv in enumerate(cpv_all):
metadata = dict(zip(meta_keys, aux_get(cpv, meta_keys)))
repo = metadata.pop('repository')
if repo_dict is None:
updates_list = updates
else:
try:
updates_list = repo_dict[repo]
except KeyError:
try:
updates_list = repo_dict['DEFAULT']
except KeyError:
continue
if not updates_list:
continue
metadata_updates = update_dbentries(updates_list, metadata)
if metadata_updates:
aux_update(cpv, metadata_updates)
if onUpdate:
onUpdate(maxval, i+1)
if onProgress:
onProgress(maxval, i+1)
def move_slot_ent(self, mylist, repo_match=None):
"""This function takes a sequence:
Args:
mylist: a sequence of (package, originalslot, newslot)
repo_match: callable that takes single repo_name argument
and returns True if the update should be applied
Returns:
The number of slotmoves this function did
"""
pkg = mylist[1]
origslot = mylist[2]
newslot = mylist[3]
origmatches = self.match(pkg)
moves = 0
if not origmatches:
return moves
for mycpv in origmatches:
slot = self.aux_get(mycpv, ["SLOT"])[0]
if slot != origslot:
continue
if repo_match is not None \
and not repo_match(self.aux_get(mycpv, ['repository'])[0]):
continue
moves += 1
mydata = {"SLOT": newslot+"\n"}
self.aux_update(mycpv, mydata)
return moves
|