Bug #233253 - Implement a @downgrade set which selects packages for which
[portage.git] / pym / portage / sets / dbapi.py
1 # Copyright 2007 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
3 # $Id$
4
5 from portage.versions import catpkgsplit, catsplit, pkgcmp
6 from portage.sets.base import PackageSet
7 from portage.sets import SetConfigError, get_boolean
8
9 __all__ = ["CategorySet", "EverythingSet", "InheritSet"]
10
11 class EverythingSet(PackageSet):
12         _operations = ["merge", "unmerge"]
13         description = "Package set which contains SLOT " + \
14                 "atoms to match all installed packages"
15
16         def __init__(self, vdbapi):
17                 super(EverythingSet, self).__init__()
18                 self._db = vdbapi
19         
20         def load(self):
21                 myatoms = []
22                 for cp in self._db.cp_all():
23                         if len(self._db.cp_list(cp)) > 1:
24                                 for cpv in self._db.cp_list(cp):
25                                         myslot = self._db.aux_get(cpv, ["SLOT"])[0]
26                                         myatoms.append(cp+":"+myslot)
27                         else:
28                                 myatoms.append(cp)
29                 self._setAtoms(myatoms)
30         
31         def singleBuilder(self, options, settings, trees):
32                 return EverythingSet(trees["vartree"].dbapi)
33         singleBuilder = classmethod(singleBuilder)
34
35 class OwnerSet(PackageSet):
36
37         _operations = ["merge", "unmerge"]
38
39         description = "Package set which contains all packages " + \
40                 "that own one or more files."
41
42         def __init__(self, vardb=None, files=None):
43                 super(OwnerSet, self).__init__()
44                 self._db = vardb
45                 self._files = files
46
47         def mapPathsToAtoms(self, paths):
48                 rValue = set()
49                 vardb = self._db
50                 aux_get = vardb.aux_get
51                 aux_keys = ["SLOT"]
52                 for link, p in vardb._owners.iter_owners(paths):
53                         cat, pn = catpkgsplit(link.mycpv)[:2]
54                         slot, = aux_get(link.mycpv, aux_keys)
55                         rValue.add("%s/%s:%s" % (cat, pn, slot))
56                 return rValue
57
58         def load(self):
59                 self._setAtoms(self.mapPathsToAtoms(self._files))
60
61         def singleBuilder(cls, options, settings, trees):
62                 if not "files" in options:
63                         raise SetConfigError("no files given")
64
65                 import shlex
66                 return cls(vardb=trees["vartree"].dbapi,
67                         files=frozenset(shlex.split(options["files"])))
68
69         singleBuilder = classmethod(singleBuilder)
70
71 class InheritSet(PackageSet):
72
73         _operations = ["merge", "unmerge"]
74
75         description = "Package set which contains all packages " + \
76                 "that inherit one or more specific eclasses."
77
78         def __init__(self, vardb=None, inherits=None):
79                 super(InheritSet, self).__init__()
80                 self._db = vardb
81                 self._inherits = inherits
82
83         def load(self):
84                 atoms = []
85                 inherits = self._inherits
86                 cp_list = self._db.cp_list
87                 aux_get = self._db.aux_get
88                 aux_keys = ["INHERITED", "SLOT"]
89                 for cp in self._db.cp_all():
90                         for cpv in cp_list(cp):
91                                 inherited, slot = aux_get(cpv, aux_keys)
92                                 inherited = inherited.split()
93                                 if inherits.intersection(inherited):
94                                         atoms.append("%s:%s" % (cp, slot))
95
96                 self._setAtoms(atoms)
97
98         def singleBuilder(cls, options, settings, trees):
99                 if not "inherits" in options:
100                         raise SetConfigError("no inherits given")
101
102                 inherits = options["inherits"]
103                 return cls(vardb=trees["vartree"].dbapi,
104                         inherits=frozenset(inherits.split()))
105
106         singleBuilder = classmethod(singleBuilder)
107
108 class DowngradeSet(PackageSet):
109
110         _operations = ["merge", "unmerge"]
111
112         description = "Package set which contains all packages " + \
113                 "for which the highest visible ebuild version is lower than " + \
114                 "the currently installed version."
115
116         def __init__(self, portdb=None, vardb=None):
117                 super(DowngradeSet, self).__init__()
118                 self._portdb = portdb
119                 self._vardb = vardb
120
121         def load(self):
122                 atoms = []
123                 xmatch = self._portdb.xmatch
124                 xmatch_level = "bestmatch-visible"
125                 cp_list = self._vardb.cp_list
126                 aux_get = self._vardb.aux_get
127                 aux_keys = ["SLOT"]
128                 for cp in self._vardb.cp_all():
129                         for cpv in cp_list(cp):
130                                 slot, = aux_get(cpv, aux_keys)
131                                 slot_atom = "%s:%s" % (cp, slot)
132                                 ebuild = xmatch(xmatch_level, slot_atom)
133                                 ebuild_split = catpkgsplit(ebuild)[1:]
134                                 installed_split = catpkgsplit(cpv)[1:]
135                                 if pkgcmp(installed_split, ebuild_split) > 0:
136                                         atoms.append(slot_atom)
137
138                 self._setAtoms(atoms)
139
140         def singleBuilder(cls, options, settings, trees):
141                 return cls(portdb=trees["porttree"].dbapi,
142                         vardb=trees["vartree"].dbapi)
143
144         singleBuilder = classmethod(singleBuilder)
145
146 class CategorySet(PackageSet):
147         _operations = ["merge", "unmerge"]
148         
149         def __init__(self, category, dbapi, only_visible=True):
150                 super(CategorySet, self).__init__()
151                 self._db = dbapi
152                 self._category = category
153                 self._check = only_visible
154                 if only_visible:
155                         s="visible"
156                 else:
157                         s="all"
158                 self.description = "Package set containing %s packages of category %s" % (s, self._category)
159                         
160         def load(self):
161                 myatoms = []
162                 for cp in self._db.cp_all():
163                         if catsplit(cp)[0] == self._category:
164                                 if (not self._check) or len(self._db.match(cp)) > 0:
165                                         myatoms.append(cp)
166                 self._setAtoms(myatoms)
167         
168         def _builderGetRepository(cls, options, repositories):
169                 repository = options.get("repository", "porttree")
170                 if not repository in repositories:
171                         raise SetConfigError("invalid repository class '%s'" % repository)
172                 return repository
173         _builderGetRepository = classmethod(_builderGetRepository)
174
175         def _builderGetVisible(cls, options):
176                 return get_boolean(options, "only_visible", True)
177         _builderGetVisible = classmethod(_builderGetVisible)
178                 
179         def singleBuilder(cls, options, settings, trees):
180                 if not "category" in options:
181                         raise SetConfigError("no category given")
182
183                 category = options["category"]
184                 if not category in settings.categories:
185                         raise SetConfigError("invalid category name '%s'" % category)
186
187                 repository = cls._builderGetRepository(options, trees.keys())
188                 visible = cls._builderGetVisible(options)
189                 
190                 return CategorySet(category, dbapi=trees[repository].dbapi, only_visible=visible)
191         singleBuilder = classmethod(singleBuilder)
192
193         def multiBuilder(cls, options, settings, trees):
194                 rValue = {}
195         
196                 if "categories" in options:
197                         categories = options["categories"].split()
198                         invalid = set(categories).difference(settings.categories)
199                         if invalid:
200                                 raise SetConfigError("invalid categories: %s" % ", ".join(list(invalid)))
201                 else:
202                         categories = settings.categories
203         
204                 repository = cls._builderGetRepository(options, trees.keys())
205                 visible = cls._builderGetVisible(options)
206                 name_pattern = options.get("name_pattern", "$category/*")
207         
208                 if not "$category" in name_pattern and not "${category}" in name_pattern:
209                         raise SetConfigError("name_pattern doesn't include $category placeholder")
210         
211                 for cat in categories:
212                         myset = CategorySet(cat, trees[repository].dbapi, only_visible=visible)
213                         myname = name_pattern.replace("$category", cat)
214                         myname = myname.replace("${category}", cat)
215                         rValue[myname] = myset
216                 return rValue
217         multiBuilder = classmethod(multiBuilder)
218