1 # Copyright 2007-2012 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
7 from portage.versions import best, catsplit, vercmp
8 from portage.dep import Atom
9 from portage.localization import _
10 from portage._sets.base import PackageSet
11 from portage._sets import SetConfigError, get_boolean
14 __all__ = ["CategorySet", "DowngradeSet",
15 "EverythingSet", "OwnerSet", "VariableSet"]
17 class EverythingSet(PackageSet):
18 _operations = ["merge"]
19 description = "Package set which contains SLOT " + \
20 "atoms to match all installed packages"
23 def __init__(self, vdbapi, **kwargs):
24 super(EverythingSet, self).__init__()
30 aux_get = self._db.aux_get
31 cp_list = self._db.cp_list
33 for cp in self._db.cp_all():
34 for cpv in cp_list(cp):
35 # NOTE: Create SLOT atoms even when there is only one
36 # SLOT installed, in order to avoid the possibility
37 # of unwanted upgrades as reported in bug #338959.
38 slot, = aux_get(cpv, db_keys)
39 atom = Atom("%s:%s" % (cp, slot))
41 if self._filter(atom):
46 self._setAtoms(myatoms)
48 def singleBuilder(self, options, settings, trees):
49 return EverythingSet(trees["vartree"].dbapi)
50 singleBuilder = classmethod(singleBuilder)
52 class OwnerSet(PackageSet):
54 _operations = ["merge", "unmerge"]
56 description = "Package set which contains all packages " + \
57 "that own one or more files."
59 def __init__(self, vardb=None, exclude_files=None, files=None):
60 super(OwnerSet, self).__init__()
62 self._exclude_files = exclude_files
65 def mapPathsToAtoms(self, paths, exclude_paths=None):
67 All paths must have $EROOT stripped from the left side.
71 aux_get = vardb.aux_get
73 if exclude_paths is None:
74 for link, p in vardb._owners.iter_owners(paths):
75 slot, = aux_get(link.mycpv, aux_keys)
76 rValue.add("%s:%s" % (link.mycpv.cp, slot))
79 all_paths.update(paths)
80 all_paths.update(exclude_paths)
82 for link, p in vardb._owners.iter_owners(all_paths):
83 slot, = aux_get(link.mycpv, aux_keys)
84 atom = "%s:%s" % (link.mycpv.cp, slot)
86 if p in exclude_paths:
87 exclude_atoms.add(atom)
88 rValue.difference_update(exclude_atoms)
93 self._setAtoms(self.mapPathsToAtoms(self._files,
94 exclude_paths=self._exclude_files))
96 def singleBuilder(cls, options, settings, trees):
97 if not "files" in options:
98 raise SetConfigError(_("no files given"))
100 exclude_files = options.get("exclude-files")
101 if exclude_files is not None:
102 exclude_files = frozenset(portage.util.shlex_split(exclude_files))
103 return cls(vardb=trees["vartree"].dbapi, exclude_files=exclude_files,
104 files=frozenset(portage.util.shlex_split(options["files"])))
106 singleBuilder = classmethod(singleBuilder)
108 class VariableSet(EverythingSet):
110 _operations = ["merge", "unmerge"]
112 description = "Package set which contains all packages " + \
113 "that match specified values of a specified variable."
115 def __init__(self, vardb, metadatadb=None, variable=None, includes=None, excludes=None):
116 super(VariableSet, self).__init__(vardb)
117 self._metadatadb = metadatadb
118 self._variable = variable
119 self._includes = includes
120 self._excludes = excludes
122 def _filter(self, atom):
123 ebuild = best(self._metadatadb.match(atom))
126 values, = self._metadatadb.aux_get(ebuild, [self._variable])
127 values = values.split()
128 if self._includes and not self._includes.intersection(values):
130 if self._excludes and self._excludes.intersection(values):
134 def singleBuilder(cls, options, settings, trees):
136 variable = options.get("variable")
138 raise SetConfigError(_("missing required attribute: 'variable'"))
140 includes = options.get("includes", "")
141 excludes = options.get("excludes", "")
143 if not (includes or excludes):
144 raise SetConfigError(_("no includes or excludes given"))
146 metadatadb = options.get("metadata-source", "vartree")
147 if not metadatadb in trees:
148 raise SetConfigError(_("invalid value '%s' for option metadata-source") % metadatadb)
150 return cls(trees["vartree"].dbapi,
151 metadatadb=trees[metadatadb].dbapi,
152 excludes=frozenset(excludes.split()),
153 includes=frozenset(includes.split()),
156 singleBuilder = classmethod(singleBuilder)
158 class DowngradeSet(PackageSet):
160 _operations = ["merge", "unmerge"]
162 description = "Package set which contains all packages " + \
163 "for which the highest visible ebuild version is lower than " + \
164 "the currently installed version."
166 def __init__(self, portdb=None, vardb=None):
167 super(DowngradeSet, self).__init__()
168 self._portdb = portdb
173 xmatch = self._portdb.xmatch
174 xmatch_level = "bestmatch-visible"
175 cp_list = self._vardb.cp_list
176 aux_get = self._vardb.aux_get
178 for cp in self._vardb.cp_all():
179 for cpv in cp_list(cp):
180 slot, = aux_get(cpv, aux_keys)
181 slot_atom = "%s:%s" % (cp, slot)
182 ebuild = xmatch(xmatch_level, slot_atom)
185 if vercmp(cpv.version, ebuild.version) > 0:
186 atoms.append(slot_atom)
188 self._setAtoms(atoms)
190 def singleBuilder(cls, options, settings, trees):
191 return cls(portdb=trees["porttree"].dbapi,
192 vardb=trees["vartree"].dbapi)
194 singleBuilder = classmethod(singleBuilder)
196 class UnavailableSet(EverythingSet):
198 _operations = ["unmerge"]
200 description = "Package set which contains all installed " + \
201 "packages for which there are no visible ebuilds " + \
202 "corresponding to the same $CATEGORY/$PN:$SLOT."
204 def __init__(self, vardb, metadatadb=None):
205 super(UnavailableSet, self).__init__(vardb)
206 self._metadatadb = metadatadb
208 def _filter(self, atom):
209 return not self._metadatadb.match(atom)
211 def singleBuilder(cls, options, settings, trees):
213 metadatadb = options.get("metadata-source", "porttree")
214 if not metadatadb in trees:
215 raise SetConfigError(_("invalid value '%s' for option "
216 "metadata-source") % (metadatadb,))
218 return cls(trees["vartree"].dbapi,
219 metadatadb=trees[metadatadb].dbapi)
221 singleBuilder = classmethod(singleBuilder)
223 class UnavailableBinaries(EverythingSet):
225 _operations = ('merge', 'unmerge',)
227 description = "Package set which contains all installed " + \
228 "packages for which corresponding binary packages " + \
231 def __init__(self, vardb, metadatadb=None):
232 super(UnavailableBinaries, self).__init__(vardb)
233 self._metadatadb = metadatadb
235 def _filter(self, atom):
236 inst_pkg = self._db.match(atom)
239 inst_cpv = inst_pkg[0]
240 return not self._metadatadb.cpv_exists(inst_cpv)
242 def singleBuilder(cls, options, settings, trees):
244 metadatadb = options.get("metadata-source", "bintree")
245 if not metadatadb in trees:
246 raise SetConfigError(_("invalid value '%s' for option "
247 "metadata-source") % (metadatadb,))
249 return cls(trees["vartree"].dbapi,
250 metadatadb=trees[metadatadb].dbapi)
252 singleBuilder = classmethod(singleBuilder)
254 class CategorySet(PackageSet):
255 _operations = ["merge", "unmerge"]
257 def __init__(self, category, dbapi, only_visible=True):
258 super(CategorySet, self).__init__()
260 self._category = category
261 self._check = only_visible
266 self.description = "Package set containing %s packages of category %s" % (s, self._category)
270 for cp in self._db.cp_all():
271 if catsplit(cp)[0] == self._category:
272 if (not self._check) or len(self._db.match(cp)) > 0:
274 self._setAtoms(myatoms)
276 def _builderGetRepository(cls, options, repositories):
277 repository = options.get("repository", "porttree")
278 if not repository in repositories:
279 raise SetConfigError(_("invalid repository class '%s'") % repository)
281 _builderGetRepository = classmethod(_builderGetRepository)
283 def _builderGetVisible(cls, options):
284 return get_boolean(options, "only_visible", True)
285 _builderGetVisible = classmethod(_builderGetVisible)
287 def singleBuilder(cls, options, settings, trees):
288 if not "category" in options:
289 raise SetConfigError(_("no category given"))
291 category = options["category"]
292 if not category in settings.categories:
293 raise SetConfigError(_("invalid category name '%s'") % category)
295 repository = cls._builderGetRepository(options, trees.keys())
296 visible = cls._builderGetVisible(options)
298 return CategorySet(category, dbapi=trees[repository].dbapi, only_visible=visible)
299 singleBuilder = classmethod(singleBuilder)
301 def multiBuilder(cls, options, settings, trees):
304 if "categories" in options:
305 categories = options["categories"].split()
306 invalid = set(categories).difference(settings.categories)
308 raise SetConfigError(_("invalid categories: %s") % ", ".join(list(invalid)))
310 categories = settings.categories
312 repository = cls._builderGetRepository(options, trees.keys())
313 visible = cls._builderGetVisible(options)
314 name_pattern = options.get("name_pattern", "$category/*")
316 if not "$category" in name_pattern and not "${category}" in name_pattern:
317 raise SetConfigError(_("name_pattern doesn't include $category placeholder"))
319 for cat in categories:
320 myset = CategorySet(cat, trees[repository].dbapi, only_visible=visible)
321 myname = name_pattern.replace("$category", cat)
322 myname = myname.replace("${category}", cat)
323 rValue[myname] = myset
325 multiBuilder = classmethod(multiBuilder)
327 class AgeSet(EverythingSet):
328 _operations = ["merge", "unmerge"]
329 _aux_keys = ('BUILD_TIME',)
331 def __init__(self, vardb, mode="older", age=7):
332 super(AgeSet, self).__init__(vardb)
336 def _filter(self, atom):
338 cpv = self._db.match(atom)[0]
340 date, = self._db.aux_get(cpv, self._aux_keys)
342 except (KeyError, ValueError):
343 return bool(self._mode == "older")
344 age = (time.time() - date) / (3600 * 24)
345 if ((self._mode == "older" and age <= self._age) \
346 or (self._mode == "newer" and age >= self._age)):
351 def singleBuilder(cls, options, settings, trees):
352 mode = options.get("mode", "older")
353 if str(mode).lower() not in ["newer", "older"]:
354 raise SetConfigError(_("invalid 'mode' value %s (use either 'newer' or 'older')") % mode)
356 age = int(options.get("age", "7"))
357 except ValueError as e:
358 raise SetConfigError(_("value of option 'age' is not an integer"))
359 return AgeSet(vardb=trees["vartree"].dbapi, mode=mode, age=age)
361 singleBuilder = classmethod(singleBuilder)
363 class DateSet(EverythingSet):
364 _operations = ["merge", "unmerge"]
365 _aux_keys = ('BUILD_TIME',)
367 def __init__(self, vardb, date, mode="older"):
368 super(DateSet, self).__init__(vardb)
372 def _filter(self, atom):
374 cpv = self._db.match(atom)[0]
376 date, = self._db.aux_get(cpv, self._aux_keys)
378 except (KeyError, ValueError):
379 return bool(self._mode == "older")
380 # Make sure inequality is _strict_ to exclude tested package
381 if ((self._mode == "older" and date < self._date) \
382 or (self._mode == "newer" and date > self._date)):
387 def singleBuilder(cls, options, settings, trees):
388 vardbapi = trees["vartree"].dbapi
389 mode = options.get("mode", "older")
390 if str(mode).lower() not in ["newer", "older"]:
391 raise SetConfigError(_("invalid 'mode' value %s (use either 'newer' or 'older')") % mode)
394 if options.get("package") is not None:
395 formats.append("package")
396 if options.get("filestamp") is not None:
397 formats.append("filestamp")
398 if options.get("seconds") is not None:
399 formats.append("seconds")
400 if options.get("date") is not None:
401 formats.append("date")
404 raise SetConfigError(_("none of these options specified: 'package', 'filestamp', 'seconds', 'date'"))
405 elif len(formats) > 1:
406 raise SetConfigError(_("no more than one of these options is allowed: 'package', 'filestamp', 'seconds', 'date'"))
410 if (format == "package"):
411 package = options.get("package")
413 cpv = vardbapi.match(package)[0]
414 date, = vardbapi.aux_get(cpv, ('BUILD_TIME',))
416 except (KeyError, ValueError):
417 raise SetConfigError(_("cannot determine installation date of package %s") % package)
418 elif (format == "filestamp"):
419 filestamp = options.get("filestamp")
421 date = int(os.stat(filestamp).st_mtime)
422 except (OSError, ValueError):
423 raise SetConfigError(_("cannot determine 'filestamp' of '%s'") % filestamp)
424 elif (format == "seconds"):
426 date = int(options.get("seconds"))
428 raise SetConfigError(_("option 'seconds' must be an integer"))
430 dateopt = options.get("date")
432 dateformat = options.get("dateformat", "%x %X")
433 date = int(time.mktime(time.strptime(dateopt, dateformat)))
435 raise SetConfigError(_("'date=%s' does not match 'dateformat=%s'") % (dateopt, dateformat))
436 return DateSet(vardb=vardbapi, date=date, mode=mode)
438 singleBuilder = classmethod(singleBuilder)
440 class RebuiltBinaries(EverythingSet):
441 _operations = ('merge',)
442 _aux_keys = ('BUILD_TIME',)
444 def __init__(self, vardb, bindb=None):
445 super(RebuiltBinaries, self).__init__(vardb, bindb=bindb)
448 def _filter(self, atom):
449 cpv = self._db.match(atom)[0]
450 inst_build_time, = self._db.aux_get(cpv, self._aux_keys)
452 bin_build_time, = self._bindb.aux_get(cpv, self._aux_keys)
455 return bool(bin_build_time and (inst_build_time != bin_build_time))
457 def singleBuilder(cls, options, settings, trees):
458 return RebuiltBinaries(trees["vartree"].dbapi,
459 bindb=trees["bintree"].dbapi)
461 singleBuilder = classmethod(singleBuilder)