EAPI="4-python" and EAPI="5-progress": Add support for use.aliases and package.use...
authorArfrever Frehtes Taifersar Arahesis <Arfrever@Apache.Org>
Thu, 15 Nov 2012 07:19:21 +0000 (08:19 +0100)
committerArfrever Frehtes Taifersar Arahesis <Arfrever@Apache.Org>
Thu, 15 Nov 2012 07:19:21 +0000 (08:19 +0100)
doc/package/ebuild/eapi/4-python.docbook
doc/package/ebuild/eapi/5-progress.docbook
pym/_emerge/Package.py
pym/_emerge/depgraph.py
pym/_emerge/resolver/slot_collision.py
pym/portage/eapi.py
pym/portage/package/ebuild/_config/UseManager.py
pym/portage/tests/resolver/ResolverPlayground.py
pym/portage/tests/resolver/test_use_aliases.py [new file with mode: 0644]

index c49033023d5ba5ed2ddf709134086cebb574239c..9e8a9daa185999b6078396dea89eb9d95aa8b7f5 100644 (file)
                        </itemizedlist>
                </para>
        </section>
+       <section id='package-ebuild-eapi-4-python-use-aliases'>
+               <title>USE Flag Aliases</title>
+               <para>
+                       USE flag aliases are supported to allow to satisfy dependencies of packages from other repositories, which require differently named USE flags. USE flag aliases are defined in ${repository_path}/profiles/use.aliases and ${repository_path}/profiles/package.use.aliases files.
+               </para>
+               <table><title>use.aliases Example</title>
+                       <tgroup cols='1' align='left'>
+                               <tbody>
+                                       <row>
+                                               <entry>real_flag1 alias1 alias2</entry>
+                                       </row>
+                                       <row>
+                                               <entry>real_flag2 alias3 alias4</entry>
+                                       </row>
+                               </tbody>
+                       </tgroup>
+               </table>
+               <table><title>package.use.aliases Example</title>
+                       <tgroup cols='1' align='left'>
+                               <tbody>
+                                       <row>
+                                               <entry>category/package1 real_flag1 alias1 alias2</entry>
+                                       </row>
+                                       <row>
+                                               <entry>category/package1 real_flag2 alias3 alias4</entry>
+                                       </row>
+                                       <row>
+                                               <entry>=category/package2-1* real_flag3 alias5 alias6</entry>
+                                       </row>
+                                       <row>
+                                               <entry>=category/package2-2* real_flag4 alias5 alias6</entry>
+                                       </row>
+                               </tbody>
+                       </tgroup>
+               </table>
+       </section>
 </section>
index 2acda95974c0e4a9e44da138d98b8c9b9ea95972..bc7c3006696f4143985a0323e4016e296fb00d86 100644 (file)
                        </itemizedlist>
                </para>
        </section>
+       <section id='package-ebuild-eapi-5-progress-use-aliases'>
+               <title>USE Flag Aliases</title>
+               <para>
+                       USE flag aliases are supported to allow to satisfy dependencies of packages from other repositories, which require differently named USE flags. USE flag aliases are defined in ${repository_path}/profiles/use.aliases and ${repository_path}/profiles/package.use.aliases files.
+               </para>
+               <table><title>use.aliases Example</title>
+                       <tgroup cols='1' align='left'>
+                               <tbody>
+                                       <row>
+                                               <entry>real_flag1 alias1 alias2</entry>
+                                       </row>
+                                       <row>
+                                               <entry>real_flag2 alias3 alias4</entry>
+                                       </row>
+                               </tbody>
+                       </tgroup>
+               </table>
+               <table><title>package.use.aliases Example</title>
+                       <tgroup cols='1' align='left'>
+                               <tbody>
+                                       <row>
+                                               <entry>category/package1 real_flag1 alias1 alias2</entry>
+                                       </row>
+                                       <row>
+                                               <entry>category/package1 real_flag2 alias3 alias4</entry>
+                                       </row>
+                                       <row>
+                                               <entry>=category/package2-1* real_flag3 alias5 alias6</entry>
+                                       </row>
+                                       <row>
+                                               <entry>=category/package2-2* real_flag4 alias5 alias6</entry>
+                                       </row>
+                               </tbody>
+                       </tgroup>
+               </table>
+       </section>
 </section>
index b60f74486684f6a269b106e878f8a30ed28eabf1..1c1840836984f6411646bc8bbcf42937ced69fdc 100644 (file)
@@ -10,7 +10,7 @@ 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
@@ -62,9 +62,13 @@ 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.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
 
@@ -72,17 +76,15 @@ class Package(Task):
                        implicit_match = self.root_config.settings._iuse_effective_match
                else:
                        implicit_match = self.root_config.settings._iuse_implicit_match
-               self.iuse = self._iuse(self.metadata["IUSE"].split(), 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()
 
@@ -469,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
@@ -542,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):
                        """
@@ -572,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
@@ -585,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
 
index 952d0618dc48122e3baa73c3182d39c8c9bea5cc..dace4fc74b322a7d4f284ccfb57be1e937fd87ca 100644 (file)
@@ -4150,17 +4150,18 @@ class depgraph(object):
                        new_changes = {}
 
                for flag, state in target_use.items():
+                       real_flag = pkg.iuse.get_real_flag(flag)
                        if state:
-                               if flag not in old_use:
-                                       if new_changes.get(flag) == False:
+                               if real_flag not in old_use:
+                                       if new_changes.get(real_flag) == False:
                                                return old_use
-                                       new_changes[flag] = True
+                                       new_changes[real_flag] = True
                                new_use.add(flag)
                        else:
-                               if flag in old_use:
-                                       if new_changes.get(flag) == True:
+                               if real_flag in old_use:
+                                       if new_changes.get(real_flag) == True:
                                                return old_use
-                                       new_changes[flag] = False
+                                       new_changes[real_flag] = False
                new_use.update(old_use.difference(target_use))
 
                def want_restart_for_use_change(pkg, new_use):
index bc95e916e26b1557b0b983c34ba5278212c29067..c2b04d58c1dfcc4d62f93e9f933c97a669e5a538 100644 (file)
@@ -225,10 +225,11 @@ class slot_conflict_handler(object):
                new_change = {}
                for pkg in solution:
                        for flag, state in solution[pkg].items():
+                               real_flag = pkg.iuse.get_real_flag(flag)
                                if state == "enabled" and flag not in _pkg_use_enabled(pkg):
-                                       new_change.setdefault(pkg, {})[flag] = True
+                                       new_change.setdefault(pkg, {})[real_flag] = True
                                elif state == "disabled" and flag in _pkg_use_enabled(pkg):
-                                       new_change.setdefault(pkg, {})[flag] = False
+                                       new_change.setdefault(pkg, {})[real_flag] = False
                return new_change
 
        def _prepare_conflict_msg_and_check_for_specificity(self):
index 4936e6cd7078734f4425e1cf029d7671a4022dfb..bc1240c90392457f1482c7f2cb3dbb27fd340008 100644 (file)
@@ -83,6 +83,9 @@ def eapi_supports_stable_use_forcing_and_masking(eapi):
 def eapi_allows_directories_on_profile_level_and_repository_level(eapi):
        return eapi in ("4-python", "5-progress")
 
+def eapi_has_use_aliases(eapi):
+       return eapi in ("4-python", "5-progress")
+
 def eapi_has_hdepend(eapi):
        return eapi in ("5-hdepend",)
 
index cd67ced93df36b27d835d3a1353a07b397731526..93ec6209cf12512114a90f6d1133e9dc5d0c97ce 100644 (file)
@@ -7,10 +7,11 @@ __all__ = (
 
 from _emerge.Package import Package
 from portage import os
-from portage.dep import dep_getrepo, dep_getslot, ExtendedAtomDict, remove_slot, _get_useflag_re
-from portage.eapi import eapi_supports_stable_use_forcing_and_masking
+from portage.dep import Atom, dep_getrepo, dep_getslot, ExtendedAtomDict, remove_slot, _get_useflag_re, _repo_separator
+from portage.eapi import eapi_has_use_aliases, eapi_supports_stable_use_forcing_and_masking
+from portage.exception import InvalidAtom
 from portage.localization import _
-from portage.util import grabfile, grabdict_package, read_corresponding_eapi_file, stack_lists, writemsg
+from portage.util import grabfile, grabdict, grabdict_package, read_corresponding_eapi_file, stack_lists, writemsg
 from portage.versions import _pkg_str
 
 from portage.package.ebuild._config.helper import ordered_by_atom_specificity
@@ -26,10 +27,12 @@ class UseManager(object):
                #       use.stable.mask                 _repo_usestablemask_dict
                #       use.force                       _repo_useforce_dict
                #       use.stable.force                _repo_usestableforce_dict
+               #       use.aliases                     _repo_usealiases_dict
                #       package.use.mask                _repo_pusemask_dict
                #       package.use.stable.mask         _repo_pusestablemask_dict
                #       package.use.force               _repo_puseforce_dict
                #       package.use.stable.force        _repo_pusestableforce_dict
+               #       package.use.aliases             _repo_pusealiases_dict
                #--------------------------------
                #       profiles
                #--------------------------------
@@ -96,6 +99,9 @@ class UseManager(object):
 
                self._pusedict = self._parse_user_files_to_extatomdict("package.use", abs_user_config, user_config)
 
+               self._repo_usealiases_dict = self._parse_repository_usealiases(repositories)
+               self._repo_pusealiases_dict = self._parse_repository_packageusealiases(repositories)
+
                self.repositories = repositories
 
        def _parse_file_to_tuple(self, file_name, recursive=True, eapi_filter=None):
@@ -200,6 +206,69 @@ class UseManager(object):
                        user_config=profile.user_config)
                        for profile in locations)
 
+       def _parse_repository_usealiases(self, repositories):
+               ret = {}
+               for repo in repositories.repos_with_profiles():
+                       file_name = os.path.join(repo.location, "profiles", "use.aliases")
+                       eapi = read_corresponding_eapi_file(file_name)
+                       useflag_re = _get_useflag_re(eapi)
+                       raw_file_dict = grabdict(file_name, recursive=True)
+                       file_dict = {}
+                       for real_flag, aliases in raw_file_dict.items():
+                               if useflag_re.match(real_flag) is None:
+                                       writemsg(_("--- Invalid real USE flag in '%s': '%s'\n") % (file_name, real_flag), noiselevel=-1)
+                               else:
+                                       for alias in aliases:
+                                               if useflag_re.match(alias) is None:
+                                                       writemsg(_("--- Invalid USE flag alias for '%s' real USE flag in '%s': '%s'\n") %
+                                                               (real_flag, file_name, alias), noiselevel=-1)
+                                               else:
+                                                       if any(alias in v for k, v in file_dict.items() if k != real_flag):
+                                                               writemsg(_("--- Duplicated USE flag alias in '%s': '%s'\n") %
+                                                                       (file_name, alias), noiselevel=-1)
+                                                       else:
+                                                               file_dict.setdefault(real_flag, []).append(alias)
+                       ret[repo.name] = file_dict
+               return ret
+
+       def _parse_repository_packageusealiases(self, repositories):
+               ret = {}
+               for repo in repositories.repos_with_profiles():
+                       file_name = os.path.join(repo.location, "profiles", "package.use.aliases")
+                       eapi = read_corresponding_eapi_file(file_name)
+                       useflag_re = _get_useflag_re(eapi)
+                       lines = grabfile(file_name, recursive=True)
+                       file_dict = {}
+                       for line in lines:
+                               elements = line.split()
+                               atom = elements[0]
+                               try:
+                                       atom = Atom(atom, eapi=eapi)
+                               except InvalidAtom:
+                                       writemsg(_("--- Invalid atom in '%s': '%s'\n") % (file_name, atom))
+                                       continue
+                               if len(elements) == 1:
+                                       writemsg(_("--- Missing real USE flag for '%s' in '%s'\n") % (atom, file_name), noiselevel=-1)
+                                       continue
+                               real_flag = elements[1]
+                               if useflag_re.match(real_flag) is None:
+                                       writemsg(_("--- Invalid real USE flag for '%s' in '%s': '%s'\n") % (atom, file_name, real_flag), noiselevel=-1)
+                               else:
+                                       for alias in elements[2:]:
+                                               if useflag_re.match(alias) is None:
+                                                       writemsg(_("--- Invalid USE flag alias for '%s' real USE flag for '%s' in '%s': '%s'\n") %
+                                                               (real_flag, atom, file_name, alias), noiselevel=-1)
+                                               else:
+                                                       # Duplicated USE flag aliases in entries for different atoms
+                                                       # matching the same package version are detected in getUseAliases().
+                                                       if any(alias in v for k, v in file_dict.get(atom.cp, {}).get(atom, {}).items() if k != real_flag):
+                                                               writemsg(_("--- Duplicated USE flag alias for '%s' in '%s': '%s'\n") %
+                                                                       (atom, file_name, alias), noiselevel=-1)
+                                                       else:
+                                                               file_dict.setdefault(atom.cp, {}).setdefault(atom, {}).setdefault(real_flag, []).append(alias)
+                       ret[repo.name] = file_dict
+               return ret
+
        def getUseMask(self, pkg=None):
                if pkg is None:
                        return frozenset(stack_lists(
@@ -327,6 +396,50 @@ class UseManager(object):
 
                return frozenset(stack_lists(useforce, incremental=True))
 
+       def getUseAliases(self, pkg):
+               if not eapi_has_use_aliases(pkg.metadata["EAPI"]):
+                       return {}
+
+               cp = getattr(pkg, "cp", None)
+               if cp is None:
+                       slot = dep_getslot(pkg)
+                       repo = dep_getrepo(pkg)
+                       pkg = _pkg_str(remove_slot(pkg), slot=slot, repo=repo)
+                       cp = pkg.cp
+
+               usealiases = {}
+
+               if hasattr(pkg, "repo") and pkg.repo != Package.UNKNOWN_REPO:
+                       repos = []
+                       try:
+                               repos.extend(repo.name for repo in
+                                       self.repositories[pkg.repo].masters)
+                       except KeyError:
+                               pass
+                       repos.append(pkg.repo)
+                       for repo in repos:
+                               usealiases_dict = self._repo_usealiases_dict.get(repo, {})
+                               for real_flag, aliases in usealiases_dict.items():
+                                       for alias in aliases:
+                                               if any(alias in v for k, v in usealiases.items() if k != real_flag):
+                                                       writemsg(_("--- Duplicated USE flag alias for '%s%s%s': '%s'\n") %
+                                                               (pkg.cpv, _repo_separator, pkg.repo, alias), noiselevel=-1)
+                                               else:
+                                                       usealiases.setdefault(real_flag, []).append(alias)
+                               cp_usealiases_dict = self._repo_pusealiases_dict.get(repo, {}).get(cp)
+                               if cp_usealiases_dict:
+                                       usealiases_dict_list = ordered_by_atom_specificity(cp_usealiases_dict, pkg)
+                                       for usealiases_dict in usealiases_dict_list:
+                                               for real_flag, aliases in usealiases_dict.items():
+                                                       for alias in aliases:
+                                                               if any(alias in v for k, v in usealiases.items() if k != real_flag):
+                                                                       writemsg(_("--- Duplicated USE flag alias for '%s%s%s': '%s'\n") %
+                                                                               (pkg.cpv, _repo_separator, pkg.repo, alias), noiselevel=-1)
+                                                               else:
+                                                                       usealiases.setdefault(real_flag, []).append(alias)
+
+               return usealiases
+
        def getPUSE(self, pkg):
                cp = getattr(pkg, "cp", None)
                if cp is None:
index 62aafb56eea6c2394d755ff52bd084b0c54435d5..9b30edbee8cd23da4dfbf54ed55cee0eb567be6b 100644 (file)
@@ -34,10 +34,10 @@ class ResolverPlayground(object):
        its work.
        """
 
-       config_files = frozenset(("eapi", "make.conf", "package.accept_keywords", "package.use",
-               "package.use.stable.mask", "package.mask", "package.keywords",
-               "package.unmask", "package.properties", "package.license", "use.mask", "use.force",
-               "layout.conf",))
+       config_files = frozenset(("eapi", "layout.conf", "make.conf", "package.accept_keywords",
+               "package.keywords", "package.license", "package.mask", "package.properties",
+               "package.unmask", "package.use", "package.use.aliases", "package.use.stable.mask",
+               "use.aliases", "use.force", "use.mask", "layout.conf"))
 
        metadata_xml_template = """<?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE pkgmetadata SYSTEM "http://www.gentoo.org/dtd/metadata.dtd">
diff --git a/pym/portage/tests/resolver/test_use_aliases.py b/pym/portage/tests/resolver/test_use_aliases.py
new file mode 100644 (file)
index 0000000..7c2debb
--- /dev/null
@@ -0,0 +1,131 @@
+# Copyright 2012 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+from portage.tests import TestCase
+from portage.tests.resolver.ResolverPlayground import ResolverPlayground, ResolverPlaygroundTestCase
+
+class UseAliasesTestCase(TestCase):
+       def testUseAliases(self):
+               ebuilds = {
+                       "dev-libs/A-1": {"DEPEND": "dev-libs/K[x]", "RDEPEND": "dev-libs/K[x]", "EAPI": "5"},
+                       "dev-libs/B-1": {"DEPEND": "dev-libs/L[x]", "RDEPEND": "dev-libs/L[x]", "EAPI": "5"},
+                       "dev-libs/C-1": {"DEPEND": "dev-libs/M[xx]", "RDEPEND": "dev-libs/M[xx]", "EAPI": "5"},
+                       "dev-libs/D-1": {"DEPEND": "dev-libs/N[-x]", "RDEPEND": "dev-libs/N[-x]", "EAPI": "5"},
+                       "dev-libs/E-1": {"DEPEND": "dev-libs/O[-xx]", "RDEPEND": "dev-libs/O[-xx]", "EAPI": "5"},
+                       "dev-libs/F-1": {"DEPEND": "dev-libs/P[-xx]", "RDEPEND": "dev-libs/P[-xx]", "EAPI": "5"},
+                       "dev-libs/G-1": {"DEPEND": "dev-libs/Q[x-y]", "RDEPEND": "dev-libs/Q[x-y]", "EAPI": "5"},
+                       "dev-libs/H-1": {"DEPEND": "=dev-libs/R-1*[yy]", "RDEPEND": "=dev-libs/R-1*[yy]", "EAPI": "5"},
+                       "dev-libs/H-2": {"DEPEND": "=dev-libs/R-2*[yy]", "RDEPEND": "=dev-libs/R-2*[yy]", "EAPI": "5"},
+                       "dev-libs/I-1": {"DEPEND": "dev-libs/S[y-z]", "RDEPEND": "dev-libs/S[y-z]", "EAPI": "5"},
+                       "dev-libs/I-2": {"DEPEND": "dev-libs/S[y_z]", "RDEPEND": "dev-libs/S[y_z]", "EAPI": "5"},
+                       "dev-libs/J-1": {"DEPEND": "dev-libs/T[x]", "RDEPEND": "dev-libs/T[x]", "EAPI": "5"},
+                       "dev-libs/K-1": {"IUSE": "+x", "EAPI": "5"},
+                       "dev-libs/K-2::repo1": {"IUSE": "+X", "EAPI": "5-progress"},
+                       "dev-libs/L-1": {"IUSE": "+x", "EAPI": "5"},
+                       "dev-libs/M-1::repo1": {"IUSE": "X", "EAPI": "5-progress"},
+                       "dev-libs/N-1": {"IUSE": "x", "EAPI": "5"},
+                       "dev-libs/N-2::repo1": {"IUSE": "X", "EAPI": "5-progress"},
+                       "dev-libs/O-1": {"IUSE": "x", "EAPI": "5"},
+                       "dev-libs/P-1::repo1": {"IUSE": "+X", "EAPI": "5-progress"},
+                       "dev-libs/Q-1::repo2": {"IUSE": "X.Y", "EAPI": "5-progress"},
+                       "dev-libs/R-1::repo1": {"IUSE": "Y", "EAPI": "5-progress"},
+                       "dev-libs/R-2::repo1": {"IUSE": "y", "EAPI": "5-progress"},
+                       "dev-libs/S-1::repo2": {"IUSE": "Y.Z", "EAPI": "5-progress"},
+                       "dev-libs/S-2::repo2": {"IUSE": "Y.Z", "EAPI": "5-progress"},
+                       "dev-libs/T-1::repo1": {"IUSE": "+X", "EAPI": "5"},
+               }
+
+               installed = {
+                       "dev-libs/L-2::repo1": {"IUSE": "+X", "USE": "X", "EAPI": "5-progress"},
+                       "dev-libs/O-2::repo1": {"IUSE": "X", "USE": "", "EAPI": "5-progress"},
+               }
+
+               repo_configs = {
+                       "repo1": {
+                               "use.aliases": ("X x xx",),
+                               "package.use.aliases": (
+                                       "=dev-libs/R-1* Y yy",
+                                       "=dev-libs/R-2* y yy",
+                               )
+                       },
+                       "repo2": {
+                               "eapi": ("5-progress",),
+                               "use.aliases": ("X.Y x-y",),
+                               "package.use.aliases": (
+                                       "=dev-libs/S-1* Y.Z y-z",
+                                       "=dev-libs/S-2* Y.Z y_z",
+                               ),
+                       },
+               }
+
+               test_cases = (
+                       ResolverPlaygroundTestCase(
+                               ["dev-libs/A"],
+                               success = True,
+                               mergelist = ["dev-libs/K-2", "dev-libs/A-1"]),
+                       ResolverPlaygroundTestCase(
+                               ["dev-libs/B"],
+                               success = True,
+                               mergelist = ["dev-libs/B-1"]),
+                       ResolverPlaygroundTestCase(
+                               ["dev-libs/C"],
+                               options = {"--autounmask": True},
+                               success = False,
+                               mergelist = ["dev-libs/M-1", "dev-libs/C-1"],
+                               use_changes = {"dev-libs/M-1": {"X": True}}),
+                       ResolverPlaygroundTestCase(
+                               ["dev-libs/D"],
+                               success = True,
+                               mergelist = ["dev-libs/N-2", "dev-libs/D-1"]),
+                       ResolverPlaygroundTestCase(
+                               ["dev-libs/E"],
+                               success = True,
+                               mergelist = ["dev-libs/E-1"]),
+                       ResolverPlaygroundTestCase(
+                               ["dev-libs/F"],
+                               options = {"--autounmask": True},
+                               success = False,
+                               mergelist = ["dev-libs/P-1", "dev-libs/F-1"],
+                               use_changes = {"dev-libs/P-1": {"X": False}}),
+                       ResolverPlaygroundTestCase(
+                               ["dev-libs/G"],
+                               options = {"--autounmask": True},
+                               success = False,
+                               mergelist = ["dev-libs/Q-1", "dev-libs/G-1"],
+                               use_changes = {"dev-libs/Q-1": {"X.Y": True}}),
+                       ResolverPlaygroundTestCase(
+                               ["=dev-libs/H-1*"],
+                               options = {"--autounmask": True},
+                               success = False,
+                               mergelist = ["dev-libs/R-1", "dev-libs/H-1"],
+                               use_changes = {"dev-libs/R-1": {"Y": True}}),
+                       ResolverPlaygroundTestCase(
+                               ["=dev-libs/H-2*"],
+                               options = {"--autounmask": True},
+                               success = False,
+                               mergelist = ["dev-libs/R-2", "dev-libs/H-2"],
+                               use_changes = {"dev-libs/R-2": {"y": True}}),
+                       ResolverPlaygroundTestCase(
+                               ["=dev-libs/I-1*"],
+                               options = {"--autounmask": True},
+                               success = False,
+                               mergelist = ["dev-libs/S-1", "dev-libs/I-1"],
+                               use_changes = {"dev-libs/S-1": {"Y.Z": True}}),
+                       ResolverPlaygroundTestCase(
+                               ["=dev-libs/I-2*"],
+                               options = {"--autounmask": True},
+                               success = False,
+                               mergelist = ["dev-libs/S-2", "dev-libs/I-2"],
+                               use_changes = {"dev-libs/S-2": {"Y.Z": True}}),
+                       ResolverPlaygroundTestCase(
+                               ["dev-libs/J"],
+                               success = False),
+               )
+
+               playground = ResolverPlayground(ebuilds=ebuilds, installed=installed, repo_configs=repo_configs)
+               try:
+                       for test_case in test_cases:
+                               playground.run_TestCase(test_case)
+                               self.assertEqual(test_case.test_success, True, test_case.fail_msg)
+               finally:
+                       playground.cleanup()