EAPI="4-python" and EAPI="5-progress": Add support for use.aliases and package.use...
[portage.git] / pym / _emerge / Package.py
index 85fc597736b23eaf4dce210ef35bbb909b3397d9..1c1840836984f6411646bc8bbcf42937ced69fdc 100644 (file)
@@ -10,8 +10,9 @@ from portage.const import EBUILD_PHASES
 from portage.dep import Atom, check_required_use, use_reduce, \
        paren_enclose, _slot_separator, _repo_separator
 from portage.versions import _pkg_str, _unknown_repo
-from portage.eapi import _get_eapi_attrs
+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:
@@ -29,18 +30,20 @@ class Package(Task):
                "root_config", "type_name",
                "category", "counter", "cp", "cpv_split",
                "inherited", "iuse", "mtime",
-               "pf", "root", "slot", "slot_abi", "slot_atom", "version") + \
+               "pf", "root", "slot", "sub_slot", "slot_atom", "version") + \
                ("_invalid", "_raw_metadata", "_masks", "_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
 
@@ -59,20 +62,29 @@ class Package(Task):
                if hasattr(self.cpv, 'slot_invalid'):
                        self._invalid_metadata('SLOT.invalid',
                                "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.slot_abi = self.cpv.slot_abi
+               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
+
+               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.metadata["EAPI"])
+
                if (self.iuse.enabled or self.iuse.disabled) and \
                        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 = Atom("%s%s%s" % (self.cp, _slot_separator, self.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()
 
@@ -190,11 +202,23 @@ class Package(Task):
                        if not v:
                                continue
                        try:
-                               validated_atoms.extend(use_reduce(v, eapi=dep_eapi,
+                               atoms = use_reduce(v, eapi=dep_eapi,
                                        matchall=True, is_valid_flag=dep_valid_flag,
-                                       token_class=Atom, flat=True))
+                                       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)))
@@ -221,14 +245,14 @@ class Package(Task):
 
                k = 'REQUIRED_USE'
                v = self.metadata.get(k)
-               if v:
+               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
@@ -353,6 +377,11 @@ class Package(Task):
 
        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
@@ -373,7 +402,7 @@ class Package(Task):
                                                _unicode_decode("%s: %s") % (k, error))
 
                        if not categorized_error:
-                               self._invalid_metadata(k + ".syntax",
+                               self._invalid_metadata(qacat,
                                        _unicode_decode("%s: %s") % (k, e))
                else:
                        # For installed packages, show the path of the file
@@ -381,7 +410,7 @@ class Package(Task):
                        # 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",
+                       self._invalid_metadata(qacat,
                                _unicode_decode("%s: %s in '%s'") % (k, e, path))
 
        def _invalid_metadata(self, msg_type, msg):
@@ -442,7 +471,11 @@ class Package(Task):
                        self._expand_hidden = None
                        self._force = None
                        self._mask = None
-                       self.enabled = frozenset(use_str.split())
+                       enabled_flags = use_str.split()
+                       if eapi_has_use_aliases(pkg.metadata["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
@@ -515,26 +548,42 @@ class Package(Task):
 
        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):
                        """
@@ -545,7 +594,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
@@ -558,11 +607,22 @@ 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):
+                       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
+                       else:
+                               raise ValueError("'%s' flag is not in IUSE and is not an alias of any flag in IUSE of '%s::%s'" %
+                                       (flag, self._pkg.cpv, self._pkg.repo))
+
        def __len__(self):
                return 4
 
@@ -615,7 +675,7 @@ class _PackageMetadataWrapper(_PackageMetadataWrapperBase):
 
        __slots__ = ("_pkg",)
        _wrapped_keys = frozenset(
-               ["COUNTER", "INHERITED", "IUSE", "USE", "_mtime_"])
+               ["COUNTER", "INHERITED", "USE", "_mtime_"])
        _use_conditional_keys = frozenset(
                ['LICENSE', 'PROPERTIES', 'PROVIDE', 'RESTRICT',])
 
@@ -684,10 +744,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_counter(self, k, v):
                if isinstance(v, basestring):
                        try: