3 # Copyright 2010 Brian Dolbec <brian.dolbec@gmail.com>
4 # Copyright(c) 2010, Gentoo Foundation
5 # Distributed under the terms of the GNU General Public License v2
9 """Provides support functions to analyse modules"""
13 from gentoolkit.dbapi import PORTDB, VARDB
14 from gentoolkit import errors
15 from gentoolkit.keyword import abs_keywords
16 #from gentoolkit.package import Package
21 def get_installed_use(cpv, use="USE"):
22 """Gets the installed USE flags from the VARDB
25 @param cpv: cat/pkg-ver
27 @param use: 1 of ["USE", "PKGUSE"]
29 @returns [] or the list of IUSE flags
31 return VARDB.aux_get(cpv,[use])[0].split()
35 """Gets the current IUSE flags from the tree
38 @param cpv: cat/pkg-ver
40 @returns [] or the list of IUSE flags
43 return PORTDB.aux_get(cpv, ["IUSE"])[0].split()
49 """Absolute value function for a USE flag
52 @param flag: the use flag to absolute.
54 @return absolute USE flag
56 if flag[0] in ["+","-"]:
62 def abs_list(the_list):
63 """Absolute value function for a USE flag list
66 @param the_list: the use flags to absolute.
68 @return absolute USE flags
71 for member in the_list:
72 r.append(abs_flag(member))
76 def filter_flags(use, use_expand_hidden, usemasked, useforced):
77 """Filter function to remove hidden or otherwise not normally
78 visible USE flags from a list.
81 @param use: the USE flag list to be filtered.
82 @type use_expand_hidden: list
83 @param use_expand_hidden: list of flags hidden.
85 @param usemasked: list of masked USE flags.
87 @param useforced: the forced USE flags.
89 @return the filtered USE flags.
91 # clean out some environment flags, since they will most probably
92 # be confusing for the user
93 for f in use_expand_hidden:
98 # clean out any arch's
99 archlist = portage.settings["PORTAGE_ARCHLIST"].split()
103 # dbl check if any from usemasked or useforced are still there
104 masked = usemasked + useforced
111 def get_all_cpv_use(cpv):
112 """Uses portage to determine final USE flags and settings for an emerge
115 @param cpv: eg cat/pkg-ver
117 @return use, use_expand_hidden, usemask, useforce
120 PORTDB.settings.unlock()
122 PORTDB.settings.setcpv(cpv, use_cache=True, mydb=portage.portdb)
123 use = portage.settings['PORTAGE_USE'].split()
124 use_expand_hidden = portage.settings["USE_EXPAND_HIDDEN"].split()
125 usemask = list(PORTDB.settings.usemask)
126 useforce = list(PORTDB.settings.useforce)
128 PORTDB.settings.reset()
129 PORTDB.settings.lock()
130 return [], [], [], []
132 PORTDB.settings.reset()
133 PORTDB.settings.lock()
134 return use, use_expand_hidden, usemask, useforce
137 def get_flags(cpv, final_setting=False):
138 """Retrieves all information needed to filter out hidded, masked, etc.
139 USE flags for a given package.
142 @param cpv: eg. cat/pkg-ver
143 @type final_setting: boolean
144 @param final_setting: used to also determine the final
145 enviroment USE flag settings and return them as well.
146 @rtype: list or list, list
147 @return IUSE or IUSE, final_flags
149 final_use, use_expand_hidden, usemasked, useforced = get_all_cpv_use(cpv)
150 iuse_flags = filter_flags(get_iuse(cpv), use_expand_hidden, usemasked, useforced)
151 #flags = filter_flags(use_flags, use_expand_hidden, usemasked, useforced)
153 final_flags = filter_flags(final_use, use_expand_hidden, usemasked, useforced)
154 return iuse_flags, final_flags
157 class FlagAnalyzer(object):
158 """Specialty functions for analysing an installed package's
159 USE flags. Can be used for single or mulitple use without
160 needing to be reset unless the system USE flags are changed.
162 @type system: list or set
163 @param system: the default system USE flags.
164 @type _get_flags: function
165 @param _get_flags: Normally defaulted, can be overriden for testing
166 @type _get_used: function
167 @param _get_used: Normally defaulted, can be overriden for testing
171 _get_flags=get_flags,
172 _get_used=get_installed_use
174 self.get_flags = _get_flags
175 self.get_used = _get_used
178 def reset(self, system):
179 """Resets the internal system USE flags and use_expand variables
180 to the new setting. The use_expand variable is handled internally.
182 @type system: list or set
183 @param system: the default system USE flags.
185 self.system = set(system)
186 self.use_expand = portage.settings['USE_EXPAND'].lower().split()
188 def analyse_cpv(self, cpv):
189 """Gets all relavent USE flag info for a cpv and breaks them down
190 into 3 sets, plus (package.use enabled), minus ( package.use disabled),
193 @param cpv: string. 'cat/pkg-ver'
195 @return (plus, minus, unset) sets of USE flags
197 installed = set(self.get_used(cpv, "USE"))
198 iuse = set(abs_list(self.get_flags(cpv)))
199 return self._analyse(installed, iuse)
201 def _analyse(self, installed, iuse):
202 """Analyses the supplied info and returns the flag settings
203 that differ from the defaults
206 @param installed: the installed with use flags
208 @param iuse: the current ebuilds IUSE
210 defaults = self.system.intersection(iuse)
211 usedflags = iuse.intersection(set(installed))
212 plus = usedflags.difference(defaults)
213 minus = defaults.difference(usedflags)
214 unset = iuse.difference(defaults, plus, minus)
215 cleaned = self.remove_expanding(unset)
216 return (plus, minus, cleaned)
218 def analyse_pkg(self, pkg):
219 """Gets all relevent USE flag info for a pkg and breaks them down
220 into 3 sets, plus (package.use enabled), minus ( package.use disabled),
223 @param pkg: gentoolkit.package.Package object
225 @return (plus, minus, unset) sets of USE flags
227 installed = set(self.pkg_used(pkg))
228 iuse = set(abs_list(self.pkg_flags(pkg)))
229 return self._analyse(installed, iuse)
231 def pkg_used(self, pkg):
232 return pkg.use().split()
234 def pkg_flags(self, pkg):
235 final_use, use_expand_hidden, usemasked, useforced = \
236 get_all_cpv_use(pkg.cpv)
237 flags = pkg.environment("IUSE", prefer_vdb=False).split()
238 return filter_flags(flags, use_expand_hidden, usemasked, useforced)
240 def redundant(self, cpv, iuse):
241 """Checks for redundant settings.
242 future function. Not yet implemented.
246 def remove_expanding(self, flags):
247 """Remove unwanted USE_EXPAND flags
250 @param flags: short list or set of USE flags
255 for expander in self.use_expand:
264 class KeywordAnalyser(object):
265 """Specialty functions for analysing the installed package db for
266 keyword useage and the packages that used them.
268 Note: should be initialized with the internal set_order() before use.
269 See internal set_order() for more details.
270 This class of functions can be used for single cpv checks or
271 used repeatedly for an entire package db.
274 @param arch: the system ARCH setting
275 @type accept_keywords: list
276 @param accept_keywords: eg. ['x86', '~x86']
277 @type get_aux: function, defaults to: VARDB.aux_get
278 @param vardb: vardb class of functions, needed=aux_get()
279 to return => KEYWORDS & USE flags for a cpv
280 = aux_get(cpv, ["KEYWORDS", "USE"])
283 # parsing order to determine appropriate keyword used for installation
284 normal_order = ['stable', 'testing', 'prefix', 'testing_prefix', 'missing']
285 prefix_order = ['prefix', 'testing_prefix', 'stable', 'testing', 'missing']
286 parse_range = list(range(len(normal_order)))
289 def __init__(self, arch, accept_keywords, vardb=VARDB):
291 self.accept_keywords = accept_keywords
294 self.parse_order = None
296 'stable': self._stable,
297 'testing': self._testing,
298 'prefix': self._prefix,
299 'testing_prefix': self._testing_prefix,
300 'missing': self._missing
304 def determine_keyword(self, keywords, used, cpv):
305 """Determine the keyword from the installed USE flags and
306 the KEYWORDS that was used to install a package.
308 @param keywords: list of keywords available to install a pkg
309 @param used: list of USE flalgs recorded for the installed pkg
311 @return a keyword or null string
317 absolute_kwds = abs_keywords(keywords)
318 kwd = list(used.intersection(absolute_kwds))
319 #if keywords == ['~ppc64']:
320 #print "Checked keywords for kwd", keywords, used, "kwd =", kwd
322 #print "Checking for kwd against portage.archlist"
323 absolute_kwds = abs_keywords(keywords)
324 # check for one against archlist then re-check
325 kwd = list(absolute_kwds.intersection(portage.archlist))
326 #print "determined keyword =", kwd
329 #print "determined keyword =", key
331 #print "kwd != 1", kwd, cpv
332 result = self._missing(self.keyword, keywords)
333 else: # too many, try to narrow them dowm
334 #print "too many kwd's, trying to match against arch"
335 _kwd = list(set(kwd).intersection(self.arch))
338 #print "found one! :)", _kwd
340 else: # try re-running the short list against archlist
341 #print "Checking kwd for _kwd against portage.archlist"
342 _kwd = list(set(kwd).intersection(portage.archlist))
343 if _kwd and len(_kwd) == 1:
344 #print "found one! :)", _kwd
347 #print " :( didn't work, _kwd =", _kwd, "giving up on:", cpv
348 result = self._missing(self.keyword, keywords)
350 while not result and i in self.parse_range:
351 parsekey = self.parse_order[i]
352 result = self.check_key[parsekey](key, keywords)
356 def _stable(self, key, keywords):
357 """test for a normal stable keyword"""
362 def _testing(self, key, keywords):
363 """test for a normal testing keyword"""
364 if ("~" + key) in keywords:
368 def _prefix(self, key, keywords):
369 """test for a stable prefix keyword"""
372 _key = '-'.join([key, self.prefix])
374 #print key, "is in", keywords
378 def _testing_prefix(self, key, keywords):
379 """test for a testing prefix keyword"""
382 _key = "~" +'-'.join([key, self.prefix])
384 #print key, "is in", keywords
388 def _missing(self, key, keywords):
389 """generates a missing keyword to return"""
390 if self.prefix and key != self.keyword:
391 _key = '-'.join([key, self.prefix])
394 #print "_missisng :( _key =", _key
397 def get_inst_keyword_cpv(self, cpv):
398 """Determines the installed with keyword for cpv
401 @param cpv: an installed CAT/PKG-VER
403 @returns a keyword determined to have been used to install cpv
405 keywords, used = self.vardb.aux_get(cpv, ["KEYWORDS", "USE"])
406 keywords = keywords.split()
408 return self._parse(keywords, used, cpv=cpv)
410 def get_inst_keyword_pkg(self, pkg):
411 """Determines the installed with keyword for cpv
413 @param pkg: gentoolkit.package.Package object
415 @returns a keyword determined to have been used to install cpv
417 keywords, used = pkg.environment(["KEYWORDS", "USE"],
418 prefer_vdb=True, fallback=False)
419 keywords = keywords.split()
421 return self._parse(keywords, used, pkg=pkg)
423 def _parse(self, keywords, used, pkg=None, cpv=None):
428 if not self.parse_order:
430 keyword = self.keyword
432 if self.arch not in used:
433 #print "Found a mismatch = ", cpv, self.arch, used
434 self.mismatched.append(_cpv)
435 if keyword in keywords:
436 #print "keyword", keyword, "is in", keywords
438 elif "~"+keyword in keywords:
439 #print "~keyword", keyword, "is in", keywords
442 keyword = self.determine_keyword(keywords, used, _cpv)
444 raise errors.GentoolkitUnknownKeyword(_cpv, ' '.join(keywords), used)
447 def set_order(self, used):
448 """Used to set the parsing order to determine a keyword
449 used for installation.
451 This is needed due to the way prefix arch's and keywords
452 work with portage. It looks for the 'prefix' flag. A positive result
453 sets it to the prefix order and keyword.
456 @param used: a list of pkg USE flags or the system USE flags"""
458 #print "SET_ORDER() Setting parse order to prefix"
460 self.parse_order = self.prefix_order
461 for key in self.accept_keywords:
462 #print "SET_ORDER() '"+key+"'"
464 #print "SET_ORDER()found prefix keyword :", key
466 prefix = key.split('-')[1]
467 #print "prefix =", prefix
469 self.keyword = '-'.join([self.arch, prefix])
471 #print "SET_ORDER() Setting parse order to normal"
472 self.parse_order = self.normal_order
473 self.keyword = self.arch
474 #print "SET_ORDER() completed: prefix =", self.prefix, ", keyword =", \
475 # self.keyword, "parse order =",self.parse_order