1 # Copyright 1999-2012 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
5 from itertools import chain
7 from portage import _encodings, _unicode_decode, _unicode_encode
8 from portage.cache.mappings import slot_dict_class
9 from portage.const import EBUILD_PHASES
10 from portage.dep import Atom, check_required_use, use_reduce, \
11 paren_enclose, _get_slot_re, _slot_separator, _repo_separator
12 from portage.versions import _pkg_str, _unknown_repo
13 from portage.eapi import _get_eapi_attrs
14 from portage.exception import InvalidDependString
15 from _emerge.Task import Task
17 if sys.hexversion >= 0x3000000:
23 __hash__ = Task.__hash__
24 __slots__ = ("built", "cpv", "depth",
25 "installed", "metadata", "onlydeps", "operation",
26 "root_config", "type_name",
27 "category", "counter", "cp", "cpv_split",
28 "inherited", "iuse", "mtime",
29 "pf", "root", "slot", "slot_atom", "version") + \
30 ("_invalid", "_raw_metadata", "_masks", "_use", "_visible")
33 "BUILD_TIME", "CHOST", "COUNTER", "DEPEND", "EAPI",
34 "INHERITED", "IUSE", "KEYWORDS",
35 "LICENSE", "PDEPEND", "PROVIDE", "RDEPEND",
36 "repository", "PROPERTIES", "RESTRICT", "SLOT", "USE",
37 "_mtime_", "DEFINED_PHASES", "REQUIRED_USE"]
39 _dep_keys = ('DEPEND', 'PDEPEND', 'RDEPEND',)
40 _use_conditional_misc_keys = ('LICENSE', 'PROPERTIES', 'RESTRICT')
41 UNKNOWN_REPO = _unknown_repo
43 def __init__(self, **kwargs):
44 Task.__init__(self, **kwargs)
45 # the SlotObject constructor assigns self.root_config from keyword args
46 # and is an instance of a '_emerge.RootConfig.RootConfig class
47 self.root = self.root_config.root
48 self._raw_metadata = _PackageMetadataWrapperBase(self.metadata)
49 self.metadata = _PackageMetadataWrapper(self, self._raw_metadata)
51 self.metadata['CHOST'] = self.root_config.settings.get('CHOST', '')
52 eapi_attrs = _get_eapi_attrs(self.metadata["EAPI"])
54 if _get_slot_re(eapi_attrs).match(slot) is None:
55 self._invalid_metadata('SLOT.invalid',
56 "SLOT: invalid value: '%s'" % slot)
57 # Avoid an InvalidAtom exception when creating slot_atom.
58 # This package instance will be masked due to empty SLOT.
60 self.cpv = _pkg_str(self.cpv, slot=slot,
61 repo=self.metadata.get('repository', ''))
63 # sync metadata with validated repo (may be UNKNOWN_REPO)
64 self.metadata['repository'] = self.cpv.repo
65 if (self.iuse.enabled or self.iuse.disabled) and \
66 not eapi_attrs.iuse_defaults:
67 if not self.installed:
68 self._invalid_metadata('EAPI.incompatible',
69 "IUSE contains defaults, but EAPI doesn't allow them")
70 self.slot_atom = portage.dep.Atom("%s%s%s" % (self.cp, _slot_separator, slot))
71 self.category, self.pf = portage.catsplit(self.cpv)
72 self.cpv_split = self.cpv.cpv_split
73 self.version = self.cpv.version
74 if self.inherited is None:
75 self.inherited = frozenset()
77 if self.operation is None:
78 if self.onlydeps or self.installed:
79 self.operation = "nomerge"
81 self.operation = "merge"
83 self._hash_key = Package._gen_hash_key(cpv=self.cpv,
84 installed=self.installed, onlydeps=self.onlydeps,
85 operation=self.operation, repo_name=self.cpv.repo,
86 root_config=self.root_config,
87 type_name=self.type_name)
88 self._hash_value = hash(self._hash_key)
90 # These are calculated on-demand, so that they are calculated
91 # after FakeVartree applies its metadata tweaks.
94 if self._invalid is None:
96 if self._invalid is None:
102 if self._masks is None:
103 self._masks = self._eval_masks()
108 if self._visible is None:
109 self._visible = self._eval_visiblity(self.masks)
113 def _gen_hash_key(cls, cpv=None, installed=None, onlydeps=None,
114 operation=None, repo_name=None, root_config=None,
115 type_name=None, **kwargs):
117 if operation is None:
118 if installed or onlydeps:
119 operation = "nomerge"
124 if root_config is not None:
125 root = root_config.root
127 raise TypeError("root_config argument is required")
129 # For installed (and binary) packages we don't care for the repo
130 # when it comes to hashing, because there can only be one cpv.
131 # So overwrite the repo_key with type_name.
132 if type_name is None:
133 raise TypeError("type_name argument is required")
134 elif type_name == "ebuild":
135 if repo_name is None:
136 raise AssertionError(
137 "Package._gen_hash_key() " + \
138 "called without 'repo_name' argument")
141 # For installed (and binary) packages we don't care for the repo
142 # when it comes to hashing, because there can only be one cpv.
143 # So overwrite the repo_key with type_name.
146 return (type_name, root, cpv, operation, repo_key)
148 def _validate_deps(self):
150 Validate deps. This does not trigger USE calculation since that
151 is expensive for ebuilds and therefore we want to avoid doing
152 in unnecessarily (like for masked packages).
154 eapi = self.metadata['EAPI']
156 dep_valid_flag = self.iuse.is_valid_flag
158 # Ignore EAPI.incompatible and conditionals missing
159 # from IUSE for installed packages since these issues
160 # aren't relevant now (re-evaluate when new EAPIs are
163 dep_valid_flag = None
165 for k in self._dep_keys:
166 v = self.metadata.get(k)
170 use_reduce(v, eapi=dep_eapi, matchall=True,
171 is_valid_flag=dep_valid_flag, token_class=Atom)
172 except InvalidDependString as e:
173 self._metadata_exception(k, e)
176 v = self.metadata.get(k)
179 use_reduce(v, eapi=dep_eapi, matchall=True,
180 is_valid_flag=dep_valid_flag, token_class=Atom)
181 except InvalidDependString as e:
182 self._invalid_metadata("PROVIDE.syntax",
183 _unicode_decode("%s: %s") % (k, e))
185 for k in self._use_conditional_misc_keys:
186 v = self.metadata.get(k)
190 use_reduce(v, eapi=dep_eapi, matchall=True,
191 is_valid_flag=dep_valid_flag)
192 except InvalidDependString as e:
193 self._metadata_exception(k, e)
196 v = self.metadata.get(k)
198 if not _get_eapi_attrs(eapi).required_use:
199 self._invalid_metadata('EAPI.incompatible',
200 "REQUIRED_USE set, but EAPI='%s' doesn't allow it" % eapi)
203 check_required_use(v, (),
204 self.iuse.is_valid_flag)
205 except InvalidDependString as e:
206 # Force unicode format string for python-2.x safety,
207 # ensuring that PortageException.__unicode__() is used
209 self._invalid_metadata(k + ".syntax",
210 _unicode_decode("%s: %s") % (k, e))
213 v = self.metadata.get(k)
216 use_reduce(v, is_src_uri=True, eapi=eapi, matchall=True,
217 is_valid_flag=self.iuse.is_valid_flag)
218 except InvalidDependString as e:
219 if not self.installed:
220 self._metadata_exception(k, e)
223 return Package(built=self.built, cpv=self.cpv, depth=self.depth,
224 installed=self.installed, metadata=self._raw_metadata,
225 onlydeps=self.onlydeps, operation=self.operation,
226 root_config=self.root_config, type_name=self.type_name)
228 def _eval_masks(self):
230 settings = self.root_config.settings
232 if self.invalid is not False:
233 masks['invalid'] = self.invalid
235 if not settings._accept_chost(self.cpv, self.metadata):
236 masks['CHOST'] = self.metadata['CHOST']
238 eapi = self.metadata["EAPI"]
239 if not portage.eapi_is_supported(eapi):
240 masks['EAPI.unsupported'] = eapi
241 if portage._eapi_is_deprecated(eapi):
242 masks['EAPI.deprecated'] = eapi
244 missing_keywords = settings._getMissingKeywords(
245 self.cpv, self.metadata)
247 masks['KEYWORDS'] = missing_keywords
250 missing_properties = settings._getMissingProperties(
251 self.cpv, self.metadata)
252 if missing_properties:
253 masks['PROPERTIES'] = missing_properties
254 except InvalidDependString:
255 # already recorded as 'invalid'
258 mask_atom = settings._getMaskAtom(self.cpv, self.metadata)
259 if mask_atom is not None:
260 masks['package.mask'] = mask_atom
263 missing_licenses = settings._getMissingLicenses(
264 self.cpv, self.metadata)
266 masks['LICENSE'] = missing_licenses
267 except InvalidDependString:
268 # already recorded as 'invalid'
276 def _eval_visiblity(self, masks):
278 if masks is not False:
280 if 'EAPI.unsupported' in masks:
283 if 'invalid' in masks:
286 if not self.installed and ( \
287 'CHOST' in masks or \
288 'EAPI.deprecated' in masks or \
289 'KEYWORDS' in masks or \
290 'PROPERTIES' in masks):
293 if 'package.mask' in masks or \
299 def get_keyword_mask(self):
300 """returns None, 'missing', or 'unstable'."""
302 missing = self.root_config.settings._getRawMissingKeywords(
303 self.cpv, self.metadata)
311 global_accept_keywords = frozenset(
312 self.root_config.settings.get("ACCEPT_KEYWORDS", "").split())
314 for keyword in missing:
315 if keyword.lstrip("~") in global_accept_keywords:
320 def isHardMasked(self):
321 """returns a bool if the cpv is in the list of
322 expanded pmaskdict[cp] available ebuilds"""
323 pmask = self.root_config.settings._getRawMaskAtom(
324 self.cpv, self.metadata)
325 return pmask is not None
327 def _metadata_exception(self, k, e):
329 # For unicode safety with python-2.x we need to avoid
330 # using the string format operator with a non-unicode
331 # format string, since that will result in the
332 # PortageException.__str__() method being invoked,
333 # followed by unsafe decoding that may result in a
334 # UnicodeDecodeError. Therefore, use _unicode_decode()
335 # to ensure that format strings are unicode, so that
336 # PortageException.__unicode__() is used when necessary
338 if not self.installed:
339 categorized_error = False
341 for error in e.errors:
342 if getattr(error, 'category', None) is None:
344 categorized_error = True
345 self._invalid_metadata(error.category,
346 _unicode_decode("%s: %s") % (k, error))
348 if not categorized_error:
349 self._invalid_metadata(k + ".syntax",
350 _unicode_decode("%s: %s") % (k, e))
352 # For installed packages, show the path of the file
353 # containing the invalid metadata, since the user may
354 # want to fix the deps by hand.
355 vardb = self.root_config.trees['vartree'].dbapi
356 path = vardb.getpath(self.cpv, filename=k)
357 self._invalid_metadata(k + ".syntax",
358 _unicode_decode("%s: %s in '%s'") % (k, e, path))
360 def _invalid_metadata(self, msg_type, msg):
361 if self._invalid is None:
363 msgs = self._invalid.get(msg_type)
366 self._invalid[msg_type] = msgs
370 if self.operation == "merge":
371 if self.type_name == "binary":
372 cpv_color = "PKG_BINARY_MERGE"
374 cpv_color = "PKG_MERGE"
375 elif self.operation == "uninstall":
376 cpv_color = "PKG_UNINSTALL"
378 cpv_color = "PKG_NOMERGE"
381 % (portage.output.colorize(cpv_color, self.cpv + _repo_separator + self.repo) , self.type_name)
383 if self.type_name == "installed":
384 if self.root_config.settings['ROOT'] != "/":
385 s += " in '%s'" % self.root_config.settings['ROOT']
386 if self.operation == "uninstall":
387 s += " scheduled for uninstall"
389 if self.operation == "merge":
390 s += " scheduled for merge"
391 if self.root_config.settings['ROOT'] != "/":
392 s += " to '%s'" % self.root_config.settings['ROOT']
396 if sys.hexversion < 0x3000000:
398 __unicode__ = __str__
401 return _unicode_encode(self.__unicode__(),
402 encoding=_encodings['content'])
404 class _use_class(object):
406 __slots__ = ("enabled", "_expand", "_expand_hidden",
407 "_force", "_pkg", "_mask")
409 # Share identical frozenset instances when available.
412 def __init__(self, pkg, use_str):
415 self._expand_hidden = None
418 self.enabled = frozenset(use_str.split())
420 # Use IUSE to validate USE settings for built packages,
421 # in case the package manager that built this package
422 # failed to do that for some reason (or in case of
424 missing_iuse = pkg.iuse.get_missing_iuse(self.enabled)
426 self.enabled = self.enabled.difference(missing_iuse)
428 def _init_force_mask(self):
429 pkgsettings = self._pkg._get_pkgsettings()
430 frozensets = self._frozensets
432 pkgsettings.get("USE_EXPAND", "").lower().split())
433 self._expand = frozensets.setdefault(s, s)
435 pkgsettings.get("USE_EXPAND_HIDDEN", "").lower().split())
436 self._expand_hidden = frozensets.setdefault(s, s)
437 s = pkgsettings.useforce
438 self._force = frozensets.setdefault(s, s)
439 s = pkgsettings.usemask
440 self._mask = frozensets.setdefault(s, s)
444 if self._expand is None:
445 self._init_force_mask()
449 def expand_hidden(self):
450 if self._expand_hidden is None:
451 self._init_force_mask()
452 return self._expand_hidden
456 if self._force is None:
457 self._init_force_mask()
462 if self._mask is None:
463 self._init_force_mask()
468 return self.metadata['repository']
471 def repo_priority(self):
472 repo_info = self.root_config.settings.repositories.prepos.get(self.repo)
473 if repo_info is None:
475 return repo_info.priority
479 if self._use is None:
480 self.metadata._init_use()
483 def _get_pkgsettings(self):
484 pkgsettings = self.root_config.trees[
485 'porttree'].dbapi.doebuild_settings
486 pkgsettings.setcpv(self)
491 __slots__ = ("__weakref__", "all", "enabled", "disabled",
492 "tokens") + ("_iuse_implicit_match",)
494 def __init__(self, tokens, iuse_implicit_match):
495 self.tokens = tuple(tokens)
496 self._iuse_implicit_match = iuse_implicit_match
503 enabled.append(x[1:])
505 disabled.append(x[1:])
508 self.enabled = frozenset(enabled)
509 self.disabled = frozenset(disabled)
510 self.all = frozenset(chain(enabled, disabled, other))
512 def is_valid_flag(self, flags):
514 @return: True if all flags are valid USE values which may
515 be specified in USE dependencies, False otherwise.
517 if isinstance(flags, basestring):
521 if not flag in self.all and \
522 not self._iuse_implicit_match(flag):
526 def get_missing_iuse(self, flags):
528 @return: A list of flags missing from IUSE.
530 if isinstance(flags, basestring):
534 if not flag in self.all and \
535 not self._iuse_implicit_match(flag):
536 missing_iuse.append(flag)
544 This is used to generate mtimedb resume mergelist entries, so we
545 limit it to 4 items for backward compatibility.
547 return iter(self._hash_key[:4])
549 def __lt__(self, other):
550 if other.cp != self.cp:
552 if portage.vercmp(self.version, other.version) < 0:
556 def __le__(self, other):
557 if other.cp != self.cp:
559 if portage.vercmp(self.version, other.version) <= 0:
563 def __gt__(self, other):
564 if other.cp != self.cp:
566 if portage.vercmp(self.version, other.version) > 0:
570 def __ge__(self, other):
571 if other.cp != self.cp:
573 if portage.vercmp(self.version, other.version) >= 0:
577 _all_metadata_keys = set(x for x in portage.auxdbkeys \
578 if not x.startswith("UNUSED_"))
579 _all_metadata_keys.update(Package.metadata_keys)
580 _all_metadata_keys = frozenset(_all_metadata_keys)
582 _PackageMetadataWrapperBase = slot_dict_class(_all_metadata_keys)
584 class _PackageMetadataWrapper(_PackageMetadataWrapperBase):
586 Detect metadata updates and synchronize Package attributes.
589 __slots__ = ("_pkg",)
590 _wrapped_keys = frozenset(
591 ["COUNTER", "INHERITED", "IUSE", "SLOT", "USE", "_mtime_"])
592 _use_conditional_keys = frozenset(
593 ['LICENSE', 'PROPERTIES', 'PROVIDE', 'RESTRICT',])
595 def __init__(self, pkg, metadata):
596 _PackageMetadataWrapperBase.__init__(self)
599 # USE is lazy, but we want it to show up in self.keys().
600 _PackageMetadataWrapperBase.__setitem__(self, 'USE', '')
602 self.update(metadata)
606 use_str = self['USE']
607 self._pkg._use = self._pkg._use_class(
611 use_str = _PackageMetadataWrapperBase.__getitem__(self, 'USE')
614 calculated_use = False
616 use_str = self._pkg._get_pkgsettings()["PORTAGE_USE"]
617 calculated_use = True
618 _PackageMetadataWrapperBase.__setitem__(self, 'USE', use_str)
619 self._pkg._use = self._pkg._use_class(
621 # Initialize these now, since USE access has just triggered
622 # setcpv, and we want to cache the result of the force/mask
623 # calculations that were done.
625 self._pkg._use._init_force_mask()
629 def __getitem__(self, k):
630 v = _PackageMetadataWrapperBase.__getitem__(self, k)
631 if k in self._use_conditional_keys:
632 if self._pkg.root_config.settings.local_config and '?' in v:
634 v = paren_enclose(use_reduce(v, uselist=self._pkg.use.enabled, \
635 is_valid_flag=self._pkg.iuse.is_valid_flag))
636 except InvalidDependString:
637 # This error should already have been registered via
638 # self._pkg._invalid_metadata().
643 elif k == 'USE' and not self._pkg.built:
645 # This is lazy because it's expensive.
650 def __setitem__(self, k, v):
651 _PackageMetadataWrapperBase.__setitem__(self, k, v)
652 if k in self._wrapped_keys:
653 getattr(self, "_set_" + k.lower())(k, v)
655 def _set_inherited(self, k, v):
656 if isinstance(v, basestring):
657 v = frozenset(v.split())
658 self._pkg.inherited = v
660 def _set_iuse(self, k, v):
661 self._pkg.iuse = self._pkg._iuse(
662 v.split(), self._pkg.root_config.settings._iuse_implicit_match)
664 def _set_slot(self, k, v):
667 def _set_counter(self, k, v):
668 if isinstance(v, basestring):
673 self._pkg.counter = v
675 def _set_use(self, k, v):
676 # Force regeneration of _use attribute
677 self._pkg._use = None
678 # Use raw metadata to restore USE conditional values
679 # to unevaluated state
680 raw_metadata = self._pkg._raw_metadata
681 for x in self._use_conditional_keys:
683 self[x] = raw_metadata[x]
687 def _set__mtime_(self, k, v):
688 if isinstance(v, basestring):
696 def properties(self):
697 return self['PROPERTIES'].split()
701 return self['RESTRICT'].split()
704 def defined_phases(self):
706 Returns tokens from DEFINED_PHASES metadata if it is defined,
707 otherwise returns a tuple containing all possible phases. This
708 makes it easy to do containment checks to see if it's safe to
709 skip execution of a given phase.
711 s = self['DEFINED_PHASES']