diff options
Diffstat (limited to 'portage_with_autodep/pym/_emerge/Package.py')
-rw-r--r-- | portage_with_autodep/pym/_emerge/Package.py | 389 |
1 files changed, 270 insertions, 119 deletions
diff --git a/portage_with_autodep/pym/_emerge/Package.py b/portage_with_autodep/pym/_emerge/Package.py index c04fa1f..c795568 100644 --- a/portage_with_autodep/pym/_emerge/Package.py +++ b/portage_with_autodep/pym/_emerge/Package.py @@ -1,81 +1,98 @@ -# Copyright 1999-2012 Gentoo Foundation +# Copyright 1999-2013 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 +from __future__ import unicode_literals + import sys from itertools import chain +import warnings + import portage from portage import _encodings, _unicode_decode, _unicode_encode from portage.cache.mappings import slot_dict_class from portage.const import EBUILD_PHASES from portage.dep import Atom, check_required_use, use_reduce, \ - paren_enclose, _slot_re, _slot_separator, _repo_separator + paren_enclose, _slot_separator, _repo_separator from portage.versions import _pkg_str, _unknown_repo -from portage.eapi import eapi_has_iuse_defaults, eapi_has_required_use +from portage.eapi import _get_eapi_attrs, eapi_has_use_aliases from portage.exception import InvalidDependString +from portage.localization import _ from _emerge.Task import Task if sys.hexversion >= 0x3000000: basestring = str long = int + _unicode = str +else: + _unicode = unicode class Package(Task): __hash__ = Task.__hash__ __slots__ = ("built", "cpv", "depth", - "installed", "metadata", "onlydeps", "operation", + "installed", "onlydeps", "operation", "root_config", "type_name", "category", "counter", "cp", "cpv_split", - "inherited", "invalid", "iuse", "masks", "mtime", - "pf", "root", "slot", "slot_atom", "version", "visible",) + \ - ("_raw_metadata", "_use",) + "inherited", "iuse", "mtime", + "pf", "root", "slot", "sub_slot", "slot_atom", "version") + \ + ("_invalid", "_masks", "_metadata", "_raw_metadata", "_use", + "_validated_atoms", "_visible") metadata_keys = [ "BUILD_TIME", "CHOST", "COUNTER", "DEPEND", "EAPI", - "INHERITED", "IUSE", "KEYWORDS", + "HDEPEND", "INHERITED", "IUSE", "KEYWORDS", "LICENSE", "PDEPEND", "PROVIDE", "RDEPEND", "repository", "PROPERTIES", "RESTRICT", "SLOT", "USE", "_mtime_", "DEFINED_PHASES", "REQUIRED_USE"] - _dep_keys = ('DEPEND', 'PDEPEND', 'RDEPEND',) + _dep_keys = ('DEPEND', 'HDEPEND', 'PDEPEND', 'RDEPEND') + _buildtime_keys = ('DEPEND', 'HDEPEND') + _runtime_keys = ('PDEPEND', 'RDEPEND') _use_conditional_misc_keys = ('LICENSE', 'PROPERTIES', 'RESTRICT') UNKNOWN_REPO = _unknown_repo def __init__(self, **kwargs): + metadata = _PackageMetadataWrapperBase(kwargs.pop('metadata')) Task.__init__(self, **kwargs) # the SlotObject constructor assigns self.root_config from keyword args # and is an instance of a '_emerge.RootConfig.RootConfig class self.root = self.root_config.root - self._raw_metadata = _PackageMetadataWrapperBase(self.metadata) - self.metadata = _PackageMetadataWrapper(self, self._raw_metadata) + self._raw_metadata = metadata + self._metadata = _PackageMetadataWrapper(self, metadata) if not self.built: - self.metadata['CHOST'] = self.root_config.settings.get('CHOST', '') - slot = self.slot - if _slot_re.match(slot) is None: + self._metadata['CHOST'] = self.root_config.settings.get('CHOST', '') + eapi_attrs = _get_eapi_attrs(self.eapi) + self.cpv = _pkg_str(self.cpv, metadata=self._metadata, + settings=self.root_config.settings) + if hasattr(self.cpv, 'slot_invalid'): self._invalid_metadata('SLOT.invalid', - "SLOT: invalid value: '%s'" % slot) - # Avoid an InvalidAtom exception when creating slot_atom. - # This package instance will be masked due to empty SLOT. - slot = '0' - self.cpv = _pkg_str(self.cpv, slot=slot, - repo=self.metadata.get('repository', '')) + "SLOT: invalid value: '%s'" % self._metadata["SLOT"]) + self.cpv_split = self.cpv.cpv_split + self.category, self.pf = portage.catsplit(self.cpv) self.cp = self.cpv.cp + self.version = self.cpv.version + self.slot = self.cpv.slot + self.sub_slot = self.cpv.sub_slot + self.slot_atom = Atom("%s%s%s" % (self.cp, _slot_separator, self.slot)) # sync metadata with validated repo (may be UNKNOWN_REPO) - self.metadata['repository'] = self.cpv.repo + self._metadata['repository'] = self.cpv.repo + + if eapi_attrs.iuse_effective: + implicit_match = self.root_config.settings._iuse_effective_match + else: + implicit_match = self.root_config.settings._iuse_implicit_match + usealiases = self.root_config.settings._use_manager.getUseAliases(self) + self.iuse = self._iuse(self, self._metadata["IUSE"].split(), implicit_match, + usealiases, self.eapi) + if (self.iuse.enabled or self.iuse.disabled) and \ - not eapi_has_iuse_defaults(self.metadata["EAPI"]): + not eapi_attrs.iuse_defaults: if not self.installed: self._invalid_metadata('EAPI.incompatible', "IUSE contains defaults, but EAPI doesn't allow them") - self.slot_atom = portage.dep.Atom("%s%s%s" % (self.cp, _slot_separator, slot)) - self.category, self.pf = portage.catsplit(self.cpv) - self.cpv_split = self.cpv.cpv_split - self.version = self.cpv.version if self.inherited is None: self.inherited = frozenset() - self._validate_deps() - self.masks = self._masks() - self.visible = self._visible(self.masks) if self.operation is None: if self.onlydeps or self.installed: self.operation = "nomerge" @@ -89,6 +106,74 @@ class Package(Task): type_name=self.type_name) self._hash_value = hash(self._hash_key) + @property + def eapi(self): + return self._metadata["EAPI"] + + @property + def build_time(self): + if not self.built: + raise AttributeError('build_time') + try: + return long(self._metadata['BUILD_TIME']) + except (KeyError, ValueError): + return 0 + + @property + def defined_phases(self): + return self._metadata.defined_phases + + @property + def properties(self): + return self._metadata.properties + + @property + def restrict(self): + return self._metadata.restrict + + @property + def metadata(self): + warnings.warn("_emerge.Package.Package.metadata is deprecated", + DeprecationWarning, stacklevel=3) + return self._metadata + + # These are calculated on-demand, so that they are calculated + # after FakeVartree applies its metadata tweaks. + @property + def invalid(self): + if self._invalid is None: + self._validate_deps() + if self._invalid is None: + self._invalid = False + return self._invalid + + @property + def masks(self): + if self._masks is None: + self._masks = self._eval_masks() + return self._masks + + @property + def visible(self): + if self._visible is None: + self._visible = self._eval_visiblity(self.masks) + return self._visible + + @property + def validated_atoms(self): + """ + Returns *all* validated atoms from the deps, regardless + of USE conditionals, with USE conditionals inside + atoms left unevaluated. + """ + if self._validated_atoms is None: + self._validate_deps() + return self._validated_atoms + + @property + def stable(self): + return self.cpv.stable + @classmethod def _gen_hash_key(cls, cpv=None, installed=None, onlydeps=None, operation=None, repo_name=None, root_config=None, @@ -123,15 +208,15 @@ class Package(Task): # So overwrite the repo_key with type_name. repo_key = type_name - return (type_name, root, cpv, operation, repo_key) + return (type_name, root, _unicode(cpv), operation, repo_key) def _validate_deps(self): """ Validate deps. This does not trigger USE calculation since that is expensive for ebuilds and therefore we want to avoid doing - in unnecessarily (like for masked packages). + it unnecessarily (like for masked packages). """ - eapi = self.metadata['EAPI'] + eapi = self.eapi dep_eapi = eapi dep_valid_flag = self.iuse.is_valid_flag if self.installed: @@ -142,28 +227,44 @@ class Package(Task): dep_eapi = None dep_valid_flag = None + validated_atoms = [] for k in self._dep_keys: - v = self.metadata.get(k) + v = self._metadata.get(k) if not v: continue try: - use_reduce(v, eapi=dep_eapi, matchall=True, - is_valid_flag=dep_valid_flag, token_class=Atom) + atoms = use_reduce(v, eapi=dep_eapi, + matchall=True, is_valid_flag=dep_valid_flag, + token_class=Atom, flat=True) except InvalidDependString as e: self._metadata_exception(k, e) + else: + validated_atoms.extend(atoms) + if not self.built: + for atom in atoms: + if not isinstance(atom, Atom): + continue + if atom.slot_operator_built: + e = InvalidDependString( + _("Improper context for slot-operator " + "\"built\" atom syntax: %s") % + (atom.unevaluated_atom,)) + self._metadata_exception(k, e) + + self._validated_atoms = tuple(set(atom for atom in + validated_atoms if isinstance(atom, Atom))) k = 'PROVIDE' - v = self.metadata.get(k) + v = self._metadata.get(k) if v: try: use_reduce(v, eapi=dep_eapi, matchall=True, is_valid_flag=dep_valid_flag, token_class=Atom) except InvalidDependString as e: - self._invalid_metadata("PROVIDE.syntax", - _unicode_decode("%s: %s") % (k, e)) + self._invalid_metadata("PROVIDE.syntax", "%s: %s" % (k, e)) for k in self._use_conditional_misc_keys: - v = self.metadata.get(k) + v = self._metadata.get(k) if not v: continue try: @@ -173,24 +274,20 @@ class Package(Task): self._metadata_exception(k, e) k = 'REQUIRED_USE' - v = self.metadata.get(k) - if v: - if not eapi_has_required_use(eapi): + v = self._metadata.get(k) + if v and not self.built: + if not _get_eapi_attrs(eapi).required_use: self._invalid_metadata('EAPI.incompatible', "REQUIRED_USE set, but EAPI='%s' doesn't allow it" % eapi) else: try: check_required_use(v, (), - self.iuse.is_valid_flag) + self.iuse.is_valid_flag, eapi=eapi) except InvalidDependString as e: - # Force unicode format string for python-2.x safety, - # ensuring that PortageException.__unicode__() is used - # when necessary. - self._invalid_metadata(k + ".syntax", - _unicode_decode("%s: %s") % (k, e)) + self._invalid_metadata(k + ".syntax", "%s: %s" % (k, e)) k = 'SRC_URI' - v = self.metadata.get(k) + v = self._metadata.get(k) if v: try: use_reduce(v, is_src_uri=True, eapi=eapi, matchall=True, @@ -205,43 +302,52 @@ class Package(Task): onlydeps=self.onlydeps, operation=self.operation, root_config=self.root_config, type_name=self.type_name) - def _masks(self): + def _eval_masks(self): masks = {} settings = self.root_config.settings - if self.invalid is not None: + if self.invalid is not False: masks['invalid'] = self.invalid - if not settings._accept_chost(self.cpv, self.metadata): - masks['CHOST'] = self.metadata['CHOST'] + if not settings._accept_chost(self.cpv, self._metadata): + masks['CHOST'] = self._metadata['CHOST'] - eapi = self.metadata["EAPI"] + eapi = self.eapi if not portage.eapi_is_supported(eapi): masks['EAPI.unsupported'] = eapi if portage._eapi_is_deprecated(eapi): masks['EAPI.deprecated'] = eapi missing_keywords = settings._getMissingKeywords( - self.cpv, self.metadata) + self.cpv, self._metadata) if missing_keywords: masks['KEYWORDS'] = missing_keywords try: missing_properties = settings._getMissingProperties( - self.cpv, self.metadata) + self.cpv, self._metadata) if missing_properties: masks['PROPERTIES'] = missing_properties except InvalidDependString: # already recorded as 'invalid' pass - mask_atom = settings._getMaskAtom(self.cpv, self.metadata) + try: + missing_restricts = settings._getMissingRestrict( + self.cpv, self._metadata) + if missing_restricts: + masks['RESTRICT'] = missing_restricts + except InvalidDependString: + # already recorded as 'invalid' + pass + + mask_atom = settings._getMaskAtom(self.cpv, self._metadata) if mask_atom is not None: masks['package.mask'] = mask_atom try: missing_licenses = settings._getMissingLicenses( - self.cpv, self.metadata) + self.cpv, self._metadata) if missing_licenses: masks['LICENSE'] = missing_licenses except InvalidDependString: @@ -249,13 +355,13 @@ class Package(Task): pass if not masks: - masks = None + masks = False return masks - def _visible(self, masks): + def _eval_visiblity(self, masks): - if masks is not None: + if masks is not False: if 'EAPI.unsupported' in masks: return False @@ -267,7 +373,8 @@ class Package(Task): 'CHOST' in masks or \ 'EAPI.deprecated' in masks or \ 'KEYWORDS' in masks or \ - 'PROPERTIES' in masks): + 'PROPERTIES' in masks or \ + 'RESTRICT' in masks): return False if 'package.mask' in masks or \ @@ -280,7 +387,7 @@ class Package(Task): """returns None, 'missing', or 'unstable'.""" missing = self.root_config.settings._getRawMissingKeywords( - self.cpv, self.metadata) + self.cpv, self._metadata) if not missing: return None @@ -301,17 +408,22 @@ class Package(Task): """returns a bool if the cpv is in the list of expanded pmaskdict[cp] available ebuilds""" pmask = self.root_config.settings._getRawMaskAtom( - self.cpv, self.metadata) + self.cpv, self._metadata) return pmask is not None def _metadata_exception(self, k, e): + if k.endswith('DEPEND'): + qacat = 'dependency.syntax' + else: + qacat = k + ".syntax" + # For unicode safety with python-2.x we need to avoid # using the string format operator with a non-unicode # format string, since that will result in the # PortageException.__str__() method being invoked, # followed by unsafe decoding that may result in a - # UnicodeDecodeError. Therefore, use _unicode_decode() + # UnicodeDecodeError. Therefore, use unicode_literals # to ensure that format strings are unicode, so that # PortageException.__unicode__() is used when necessary # in python-2.x. @@ -323,27 +435,25 @@ class Package(Task): continue categorized_error = True self._invalid_metadata(error.category, - _unicode_decode("%s: %s") % (k, error)) + "%s: %s" % (k, error)) if not categorized_error: - self._invalid_metadata(k + ".syntax", - _unicode_decode("%s: %s") % (k, e)) + self._invalid_metadata(qacat,"%s: %s" % (k, e)) else: # For installed packages, show the path of the file # containing the invalid metadata, since the user may # want to fix the deps by hand. vardb = self.root_config.trees['vartree'].dbapi path = vardb.getpath(self.cpv, filename=k) - self._invalid_metadata(k + ".syntax", - _unicode_decode("%s: %s in '%s'") % (k, e, path)) + self._invalid_metadata(qacat, "%s: %s in '%s'" % (k, e, path)) def _invalid_metadata(self, msg_type, msg): - if self.invalid is None: - self.invalid = {} - msgs = self.invalid.get(msg_type) + if self._invalid is None: + self._invalid = {} + msgs = self._invalid.get(msg_type) if msgs is None: msgs = [] - self.invalid[msg_type] = msgs + self._invalid[msg_type] = msgs msgs.append(msg) def __str__(self): @@ -389,13 +499,16 @@ class Package(Task): # Share identical frozenset instances when available. _frozensets = {} - def __init__(self, pkg, use_str): + def __init__(self, pkg, enabled_flags): self._pkg = pkg self._expand = None self._expand_hidden = None self._force = None self._mask = None - self.enabled = frozenset(use_str.split()) + if eapi_has_use_aliases(pkg.eapi): + for enabled_flag in enabled_flags: + enabled_flags.extend(pkg.iuse.alias_mapping.get(enabled_flag, [])) + self.enabled = frozenset(enabled_flags) if pkg.built: # Use IUSE to validate USE settings for built packages, # in case the package manager that built this package @@ -445,7 +558,7 @@ class Package(Task): @property def repo(self): - return self.metadata['repository'] + return self._metadata['repository'] @property def repo_priority(self): @@ -457,7 +570,7 @@ class Package(Task): @property def use(self): if self._use is None: - self.metadata._init_use() + self._init_use() return self._use def _get_pkgsettings(self): @@ -466,28 +579,81 @@ class Package(Task): pkgsettings.setcpv(self) return pkgsettings + def _init_use(self): + if self.built: + # 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). The enabled flags must be consistent + # with implicit IUSE, in order to avoid potential + # inconsistencies in USE dep matching (see bug #453400). + use_str = self._metadata['USE'] + is_valid_flag = self.iuse.is_valid_flag + enabled_flags = [x for x in use_str.split() if is_valid_flag(x)] + use_str = " ".join(enabled_flags) + self._use = self._use_class( + self, enabled_flags) + else: + try: + use_str = _PackageMetadataWrapperBase.__getitem__( + self._metadata, 'USE') + except KeyError: + use_str = None + calculated_use = False + if not use_str: + use_str = self._get_pkgsettings()["PORTAGE_USE"] + calculated_use = True + self._use = self._use_class( + self, use_str.split()) + # Initialize these now, since USE access has just triggered + # setcpv, and we want to cache the result of the force/mask + # calculations that were done. + if calculated_use: + self._use._init_force_mask() + + _PackageMetadataWrapperBase.__setitem__( + self._metadata, 'USE', use_str) + + return use_str + class _iuse(object): - __slots__ = ("__weakref__", "all", "enabled", "disabled", - "tokens") + ("_iuse_implicit_match",) + __slots__ = ("__weakref__", "_iuse_implicit_match", "_pkg", "alias_mapping", + "all", "all_aliases", "enabled", "disabled", "tokens") - def __init__(self, tokens, iuse_implicit_match): + def __init__(self, pkg, tokens, iuse_implicit_match, aliases, eapi): + self._pkg = pkg self.tokens = tuple(tokens) self._iuse_implicit_match = iuse_implicit_match enabled = [] disabled = [] other = [] + enabled_aliases = [] + disabled_aliases = [] + other_aliases = [] + aliases_supported = eapi_has_use_aliases(eapi) + self.alias_mapping = {} for x in tokens: prefix = x[:1] if prefix == "+": enabled.append(x[1:]) + if aliases_supported: + self.alias_mapping[x[1:]] = aliases.get(x[1:], []) + enabled_aliases.extend(self.alias_mapping[x[1:]]) elif prefix == "-": disabled.append(x[1:]) + if aliases_supported: + self.alias_mapping[x[1:]] = aliases.get(x[1:], []) + disabled_aliases.extend(self.alias_mapping[x[1:]]) else: other.append(x) - self.enabled = frozenset(enabled) - self.disabled = frozenset(disabled) + if aliases_supported: + self.alias_mapping[x] = aliases.get(x, []) + other_aliases.extend(self.alias_mapping[x]) + self.enabled = frozenset(chain(enabled, enabled_aliases)) + self.disabled = frozenset(chain(disabled, disabled_aliases)) self.all = frozenset(chain(enabled, disabled, other)) + self.all_aliases = frozenset(chain(enabled_aliases, disabled_aliases, other_aliases)) def is_valid_flag(self, flags): """ @@ -498,7 +664,7 @@ class Package(Task): flags = [flags] for flag in flags: - if not flag in self.all and \ + if not flag in self.all and not flag in self.all_aliases and \ not self._iuse_implicit_match(flag): return False return True @@ -511,11 +677,28 @@ class Package(Task): flags = [flags] missing_iuse = [] for flag in flags: - if not flag in self.all and \ + if not flag in self.all and not flag in self.all_aliases and \ not self._iuse_implicit_match(flag): missing_iuse.append(flag) return missing_iuse + def get_real_flag(self, flag): + """ + Returns the flag's name within the scope of this package + (accounting for aliases), or None if the flag is unknown. + """ + if flag in self.all: + return flag + elif flag in self.all_aliases: + for k, v in self.alias_mapping.items(): + if flag in v: + return k + + if self._iuse_implicit_match(flag): + return flag + + return None + def __len__(self): return 4 @@ -568,7 +751,7 @@ class _PackageMetadataWrapper(_PackageMetadataWrapperBase): __slots__ = ("_pkg",) _wrapped_keys = frozenset( - ["COUNTER", "INHERITED", "IUSE", "SLOT", "USE", "_mtime_"]) + ["COUNTER", "INHERITED", "USE", "_mtime_"]) _use_conditional_keys = frozenset( ['LICENSE', 'PROPERTIES', 'PROVIDE', 'RESTRICT',]) @@ -581,31 +764,6 @@ class _PackageMetadataWrapper(_PackageMetadataWrapperBase): self.update(metadata) - def _init_use(self): - if self._pkg.built: - use_str = self['USE'] - self._pkg._use = self._pkg._use_class( - self._pkg, use_str) - else: - try: - use_str = _PackageMetadataWrapperBase.__getitem__(self, 'USE') - except KeyError: - use_str = None - calculated_use = False - if not use_str: - use_str = self._pkg._get_pkgsettings()["PORTAGE_USE"] - calculated_use = True - _PackageMetadataWrapperBase.__setitem__(self, 'USE', use_str) - self._pkg._use = self._pkg._use_class( - self._pkg, use_str) - # Initialize these now, since USE access has just triggered - # setcpv, and we want to cache the result of the force/mask - # calculations that were done. - if calculated_use: - self._pkg._use._init_force_mask() - - return use_str - def __getitem__(self, k): v = _PackageMetadataWrapperBase.__getitem__(self, k) if k in self._use_conditional_keys: @@ -623,7 +781,7 @@ class _PackageMetadataWrapper(_PackageMetadataWrapperBase): elif k == 'USE' and not self._pkg.built: if not v: # This is lazy because it's expensive. - v = self._init_use() + v = self._pkg._init_use() return v @@ -637,13 +795,6 @@ class _PackageMetadataWrapper(_PackageMetadataWrapperBase): v = frozenset(v.split()) self._pkg.inherited = v - def _set_iuse(self, k, v): - self._pkg.iuse = self._pkg._iuse( - v.split(), self._pkg.root_config.settings._iuse_implicit_match) - - def _set_slot(self, k, v): - self._pkg.slot = v - def _set_counter(self, k, v): if isinstance(v, basestring): try: |