EAPI="4-python" and EAPI="5-progress": Add support for use.aliases and package.use...
[portage.git] / pym / portage / tests / resolver / ResolverPlayground.py
index ec6f63166c3032bc8596e069c370750b96d28a91..9b30edbee8cd23da4dfbf54ed55cee0eb567be6b 100644 (file)
@@ -1,23 +1,20 @@
-# Copyright 2010-2011 Gentoo Foundation
+# Copyright 2010-2012 Gentoo Foundation
 # Distributed under the terms of the GNU General Public License v2
 
 from itertools import permutations
-import shutil
 import sys
 import tempfile
 import portage
 from portage import os
+from portage import shutil
 from portage.const import (GLOBAL_CONFIG_PATH, PORTAGE_BASE_PATH,
        USER_CONFIG_PATH)
-from portage.dbapi.vartree import vartree
-from portage.dbapi.porttree import portagetree
-from portage.dbapi.bintree import binarytree
 from portage.dep import Atom, _repo_separator
 from portage.package.ebuild.config import config
 from portage.package.ebuild.digestgen import digestgen
 from portage._sets import load_default_config
 from portage._sets.base import InternalPackageSet
-from portage.util import ensure_dirs
+from portage.util import ensure_dirs, normalize_path
 from portage.versions import catsplit
 
 import _emerge
@@ -37,9 +34,10 @@ class ResolverPlayground(object):
        its work.
        """
 
-       config_files = frozenset(("package.accept_keywords", "package.use", "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">
@@ -56,8 +54,9 @@ class ResolverPlayground(object):
 </pkgmetadata>
 """
 
-       def __init__(self, ebuilds={}, installed={}, profile={}, repo_configs={}, \
-               user_config={}, sets={}, world=[], world_sets=[], distfiles={}, debug=False):
+       def __init__(self, ebuilds={}, binpkgs={}, installed={}, profile={}, repo_configs={}, \
+               user_config={}, sets={}, world=[], world_sets=[], distfiles={},
+               targetroot=False, debug=False):
                """
                ebuilds: cpv -> metadata mapping simulating available ebuilds. 
                installed: cpv -> metadata mapping simulating installed packages.
@@ -65,10 +64,14 @@ class ResolverPlayground(object):
                profile: settings defined by the profile.
                """
                self.debug = debug
-               self.root = "/"
-               self.eprefix = tempfile.mkdtemp()
-               self.eroot = self.root + self.eprefix.lstrip(os.sep) + os.sep
+               self.eprefix = normalize_path(tempfile.mkdtemp())
+               self.eroot = self.eprefix + os.sep
+               if targetroot:
+                       self.target_root = os.path.join(self.eroot, 'target_root')
+               else:
+                       self.target_root = os.sep
                self.distdir = os.path.join(self.eroot, "var", "portage", "distfiles")
+               self.pkgdir = os.path.join(self.eprefix, "pkgdir")
                self.portdir = os.path.join(self.eroot, "usr/portage")
                self.vdbdir = os.path.join(self.eroot, "var/db/pkg")
                os.makedirs(self.portdir)
@@ -83,6 +86,7 @@ class ResolverPlayground(object):
 
                self._create_distfiles(distfiles)
                self._create_ebuilds(ebuilds)
+               self._create_binpkgs(binpkgs)
                self._create_installed(installed)
                self._create_profile(ebuilds, installed, profile, repo_configs, user_config, sets)
                self._create_world(world, world_sets)
@@ -133,24 +137,18 @@ class ResolverPlayground(object):
 
                        metadata = ebuilds[cpv].copy()
                        copyright_header = metadata.pop("COPYRIGHT_HEADER", None)
-                       desc = metadata.pop("DESCRIPTION", None)
-                       eapi = metadata.pop("EAPI", 0)
-                       lic = metadata.pop("LICENSE", "")
-                       properties = metadata.pop("PROPERTIES", "")
-                       slot = metadata.pop("SLOT", 0)
-                       keywords = metadata.pop("KEYWORDS", "x86")
-                       homepage = metadata.pop("HOMEPAGE", None)
-                       src_uri = metadata.pop("SRC_URI", None)
-                       iuse = metadata.pop("IUSE", "")
-                       provide = metadata.pop("PROVIDE", None)
-                       depend = metadata.pop("DEPEND", "")
-                       rdepend = metadata.pop("RDEPEND", None)
-                       pdepend = metadata.pop("PDEPEND", None)
-                       required_use = metadata.pop("REQUIRED_USE", None)
+                       eapi = metadata.pop("EAPI", "0")
                        misc_content = metadata.pop("MISC_CONTENT", None)
+                       metadata.setdefault("DEPEND", "")
+                       metadata.setdefault("SLOT", "0")
+                       metadata.setdefault("KEYWORDS", "x86")
+                       metadata.setdefault("IUSE", "")
 
-                       if metadata:
-                               raise ValueError("metadata of ebuild '%s' contains unknown keys: %s" % (cpv, metadata.keys()))
+                       unknown_keys = set(metadata).difference(
+                               portage.dbapi.dbapi._known_keys)
+                       if unknown_keys:
+                               raise ValueError("metadata of ebuild '%s' contains unknown keys: %s" %
+                                       (cpv, sorted(unknown_keys)))
 
                        repo_dir = self._get_repo_dir(repo)
                        ebuild_dir = os.path.join(repo_dir, a.cp)
@@ -163,27 +161,9 @@ class ResolverPlayground(object):
                        f = open(ebuild_path, "w")
                        if copyright_header is not None:
                                f.write(copyright_header)
-                       f.write('EAPI="' + str(eapi) + '"\n')
-                       if desc is not None:
-                               f.write('DESCRIPTION="%s"\n' % desc)
-                       if homepage is not None:
-                               f.write('HOMEPAGE="%s"\n' % homepage)
-                       if src_uri is not None:
-                               f.write('SRC_URI="%s"\n' % src_uri)
-                       f.write('LICENSE="' + str(lic) + '"\n')
-                       f.write('PROPERTIES="' + str(properties) + '"\n')
-                       f.write('SLOT="' + str(slot) + '"\n')
-                       f.write('KEYWORDS="' + str(keywords) + '"\n')
-                       f.write('IUSE="' + str(iuse) + '"\n')
-                       if provide is not None:
-                               f.write('PROVIDE="%s"\n' % provide)
-                       f.write('DEPEND="' + str(depend) + '"\n')
-                       if rdepend is not None:
-                               f.write('RDEPEND="' + str(rdepend) + '"\n')
-                       if pdepend is not None:
-                               f.write('PDEPEND="' + str(pdepend) + '"\n')
-                       if required_use is not None:
-                               f.write('REQUIRED_USE="' + str(required_use) + '"\n')
+                       f.write('EAPI="%s"\n' % eapi)
+                       for k, v in metadata.items():
+                               f.write('%s="%s"\n' % (k, v))
                        if misc_content is not None:
                                f.write(misc_content)
                        f.close()
@@ -201,11 +181,34 @@ class ResolverPlayground(object):
                        ebuild_dir = os.path.join(repo_dir, a.cp)
                        ebuild_path = os.path.join(ebuild_dir, a.cpv.split("/")[1] + ".ebuild")
 
-                       portdb = self.trees[self.root]["porttree"].dbapi
+                       portdb = self.trees[self.eroot]["porttree"].dbapi
                        tmpsettings['O'] = ebuild_dir
                        if not digestgen(mysettings=tmpsettings, myportdb=portdb):
                                raise AssertionError('digest creation failed for %s' % ebuild_path)
 
+       def _create_binpkgs(self, binpkgs):
+               for cpv, metadata in binpkgs.items():
+                       a = Atom("=" + cpv, allow_repo=True)
+                       repo = a.repo
+                       if repo is None:
+                               repo = "test_repo"
+
+                       cat, pf = catsplit(a.cpv)
+                       metadata = metadata.copy()
+                       metadata.setdefault("SLOT", "0")
+                       metadata.setdefault("KEYWORDS", "x86")
+                       metadata.setdefault("BUILD_TIME", "0")
+                       metadata["repository"] = repo
+                       metadata["CATEGORY"] = cat
+                       metadata["PF"] = pf
+
+                       repo_dir = self.pkgdir
+                       category_dir = os.path.join(repo_dir, cat)
+                       binpkg_path = os.path.join(category_dir, pf + ".tbz2")
+                       ensure_dirs(category_dir)
+                       t = portage.xpak.tbz2(binpkg_path)
+                       t.recompose_mem(portage.xpak.xpak_mem(metadata))
+
        def _create_installed(self, installed):
                for cpv in installed:
                        a = Atom("=" + cpv, allow_repo=True)
@@ -220,47 +223,25 @@ class ResolverPlayground(object):
                                pass
 
                        metadata = installed[cpv].copy()
-                       eapi = metadata.pop("EAPI", 0)
-                       lic = metadata.pop("LICENSE", "")
-                       properties = metadata.pop("PROPERTIES", "")
-                       slot = metadata.pop("SLOT", 0)
-                       keywords = metadata.pop("KEYWORDS", "~x86")
-                       iuse = metadata.pop("IUSE", "")
-                       use = metadata.pop("USE", "")
-                       provide = metadata.pop("PROVIDE", None)
-                       depend = metadata.pop("DEPEND", "")
-                       rdepend = metadata.pop("RDEPEND", None)
-                       pdepend = metadata.pop("PDEPEND", None)
-                       required_use = metadata.pop("REQUIRED_USE", None)
-
-                       if metadata:
-                               raise ValueError("metadata of installed '%s' contains unknown keys: %s" % (cpv, metadata.keys()))
-
-                       def write_key(key, value):
-                               f = open(os.path.join(vdb_pkg_dir, key), "w")
-                               f.write(str(value) + "\n")
-                               f.close()
-                       
-                       write_key("EAPI", eapi)
-                       write_key("COUNTER", "0")
-                       write_key("LICENSE", lic)
-                       write_key("PROPERTIES", properties)
-                       write_key("SLOT", slot)
-                       write_key("LICENSE", lic)
-                       write_key("PROPERTIES", properties)
-                       write_key("repository", repo)
-                       write_key("KEYWORDS", keywords)
-                       write_key("IUSE", iuse)
-                       write_key("USE", use)
-                       if provide is not None:
-                               write_key("PROVIDE", provide)
-                       write_key("DEPEND", depend)
-                       if rdepend is not None:
-                               write_key("RDEPEND", rdepend)
-                       if pdepend is not None:
-                               write_key("PDEPEND", pdepend)
-                       if required_use is not None:
-                               write_key("REQUIRED_USE", required_use)
+                       metadata.setdefault("SLOT", "0")
+                       metadata.setdefault("BUILD_TIME", "0")
+                       metadata.setdefault("COUNTER", "0")
+                       metadata.setdefault("KEYWORDS", "~x86")
+
+                       unknown_keys = set(metadata).difference(
+                               portage.dbapi.dbapi._known_keys)
+                       unknown_keys.discard("BUILD_TIME")
+                       unknown_keys.discard("COUNTER")
+                       unknown_keys.discard("repository")
+                       unknown_keys.discard("USE")
+                       if unknown_keys:
+                               raise ValueError("metadata of installed '%s' contains unknown keys: %s" %
+                                       (cpv, sorted(unknown_keys)))
+
+                       metadata["repository"] = repo
+                       for k, v in metadata.items():
+                               with open(os.path.join(vdb_pkg_dir, k), "w") as f:
+                                       f.write("%s\n" % v)
 
        def _create_profile(self, ebuilds, installed, profile, repo_configs, user_config, sets):
 
@@ -321,10 +302,11 @@ class ResolverPlayground(object):
                                sub_profile_dir = os.path.join(profile_dir, "default", "linux", "x86", "test_profile")
                                os.makedirs(sub_profile_dir)
 
-                               eapi_file = os.path.join(sub_profile_dir, "eapi")
-                               f = open(eapi_file, "w")
-                               f.write("0\n")
-                               f.close()
+                               if not (profile and "eapi" in profile):
+                                       eapi_file = os.path.join(sub_profile_dir, "eapi")
+                                       f = open(eapi_file, "w")
+                                       f.write("0\n")
+                                       f.close()
 
                                make_defaults_file = os.path.join(sub_profile_dir, "make.defaults")
                                f = open(make_defaults_file, "w")
@@ -377,20 +359,60 @@ class ResolverPlayground(object):
                                with open(os.path.join(metadata_dir, "metadata.xml"), 'w') as f:
                                        f.write(herds_xml)
 
+               # Write empty entries for each repository, in order to exercise
+               # RepoConfigLoader's repos.conf processing.
                repos_conf_file = os.path.join(user_config_dir, "repos.conf")           
                f = open(repos_conf_file, "w")
-               priority = 0
                for repo in sorted(self.repo_dirs.keys()):
                        f.write("[%s]\n" % repo)
-                       f.write("LOCATION=%s\n" % self.repo_dirs[repo])
-                       if repo == "test_repo":
-                               f.write("PRIORITY=%s\n" % -1000)
-                       else:
-                               f.write("PRIORITY=%s\n" % priority)
-                               priority += 1
+                       f.write("\n")
                f.close()
 
-               for config_file, lines in user_config.items():
+               portdir_overlay = []
+               for repo_name in sorted(self.repo_dirs):
+                       path = self.repo_dirs[repo_name]
+                       if path != self.portdir:
+                               portdir_overlay.append(path)
+
+               make_conf = {
+                       "ACCEPT_KEYWORDS": "x86",
+                       "CLEAN_DELAY": "0",
+                       "DISTDIR" : self.distdir,
+                       "EMERGE_WARNING_DELAY": "0",
+                       "PKGDIR": self.pkgdir,
+                       "PORTDIR": self.portdir,
+                       "PORTAGE_INST_GID": str(portage.data.portage_gid),
+                       "PORTAGE_INST_UID": str(portage.data.portage_uid),
+                       "PORTDIR_OVERLAY": " ".join("'%s'" % x for x in portdir_overlay),
+                       "PORTAGE_TMPDIR": os.path.join(self.eroot, 'var/tmp'),
+               }
+
+               if os.environ.get("NOCOLOR"):
+                       make_conf["NOCOLOR"] = os.environ["NOCOLOR"]
+
+               # Pass along PORTAGE_USERNAME and PORTAGE_GRPNAME since they
+               # need to be inherited by ebuild subprocesses.
+               if 'PORTAGE_USERNAME' in os.environ:
+                       make_conf['PORTAGE_USERNAME'] = os.environ['PORTAGE_USERNAME']
+               if 'PORTAGE_GRPNAME' in os.environ:
+                       make_conf['PORTAGE_GRPNAME'] = os.environ['PORTAGE_GRPNAME']
+
+               make_conf_lines = []
+               for k_v in make_conf.items():
+                       make_conf_lines.append('%s="%s"' % k_v)
+
+               if "make.conf" in user_config:
+                       make_conf_lines.extend(user_config["make.conf"])
+
+               if not portage.process.sandbox_capable or \
+                       os.environ.get("SANDBOX_ON") == "1":
+                       # avoid problems from nested sandbox instances
+                       make_conf_lines.append('FEATURES="${FEATURES} -sandbox"')
+
+               configs = user_config.copy()
+               configs["make.conf"] = make_conf_lines
+
+               for config_file, lines in configs.items():
                        if config_file not in self.config_files:
                                raise ValueError("Unknown config file: '%s'" % config_file)
 
@@ -433,23 +455,6 @@ class ResolverPlayground(object):
                                f.write("%s\n" % line)
                        f.close()
 
-               user_config_dir = os.path.join(self.eroot, "etc", "portage")
-
-               try:
-                       os.makedirs(user_config_dir)
-               except os.error:
-                       pass
-
-               for config_file, lines in user_config.items():
-                       if config_file not in self.config_files:
-                               raise ValueError("Unknown config file: '%s'" % config_file)
-
-                       file_name = os.path.join(user_config_dir, config_file)
-                       f = open(file_name, "w")
-                       for line in lines:
-                               f.write("%s\n" % line)
-                       f.close()
-
        def _create_world(self, world, world_sets):
                #Create /var/lib/portage/world
                var_lib_portage = os.path.join(self.eroot, "var", "lib", "portage")
@@ -469,47 +474,21 @@ class ResolverPlayground(object):
                f.close()
 
        def _load_config(self):
-               portdir_overlay = []
-               for repo_name in sorted(self.repo_dirs):
-                       path = self.repo_dirs[repo_name]
-                       if path != self.portdir:
-                               portdir_overlay.append(path)
-
-               env = {
-                       "ACCEPT_KEYWORDS": "x86",
-                       "DISTDIR" : self.distdir,
-                       "PORTDIR": self.portdir,
-                       "PORTDIR_OVERLAY": " ".join(portdir_overlay),
-                       'PORTAGE_TMPDIR'       : os.path.join(self.eroot, 'var/tmp'),
-               }
 
-               # Pass along PORTAGE_USERNAME and PORTAGE_GRPNAME since they
-               # need to be inherited by ebuild subprocesses.
-               if 'PORTAGE_USERNAME' in os.environ:
-                       env['PORTAGE_USERNAME'] = os.environ['PORTAGE_USERNAME']
-               if 'PORTAGE_GRPNAME' in os.environ:
-                       env['PORTAGE_GRPNAME'] = os.environ['PORTAGE_GRPNAME']
+               create_trees_kwargs = {}
+               if self.target_root != os.sep:
+                       create_trees_kwargs["target_root"] = self.target_root
 
-               settings = config(_eprefix=self.eprefix, env=env)
-               settings.lock()
-
-               trees = {
-                       self.root: {
-                                       "vartree": vartree(settings=settings),
-                                       "porttree": portagetree(self.root, settings=settings),
-                                       "bintree": binarytree(self.root,
-                                               os.path.join(self.eroot, "usr/portage/packages"),
-                                               settings=settings)
-                               }
-                       }
+               trees = portage.create_trees(env={}, eprefix=self.eprefix,
+                       **create_trees_kwargs)
 
                for root, root_trees in trees.items():
                        settings = root_trees["vartree"].settings
                        settings._init_dirs()
                        setconfig = load_default_config(settings, root_trees)
                        root_trees["root_config"] = RootConfig(settings, root_trees, setconfig)
-               
-               return settings, trees
+
+               return trees[trees._target_eroot]["vartree"].settings, trees
 
        def run(self, atoms, options={}, action=None):
                options = options.copy()
@@ -523,6 +502,9 @@ class ResolverPlayground(object):
                        elif options.get("--prune"):
                                action = "prune"
 
+               if "--usepkgonly" in options:
+                       options["--usepkg"] = True
+
                global_noiselimit = portage.util.noiselimit
                global_emergelog_disable = _emerge.emergelog._disable
                try:
@@ -559,9 +541,10 @@ class ResolverPlayground(object):
                                return
 
        def cleanup(self):
-               portdb = self.trees[self.root]["porttree"].dbapi
-               portdb.close_caches()
-               portage.dbapi.porttree.portdbapi.portdbapi_instances.remove(portdb)
+               for eroot in self.trees:
+                       portdb = self.trees[eroot]["porttree"].dbapi
+                       portdb.close_caches()
+                       portage.dbapi.porttree.portdbapi.portdbapi_instances.remove(portdb)
                if self.debug:
                        print("\nEROOT=%s" % self.eroot)
                else:
@@ -611,8 +594,7 @@ class ResolverPlaygroundTestCase(object):
                                                        if cpv[:1] == "!":
                                                                new_got.append(cpv)
                                                                continue
-                                                       a = Atom("="+cpv, allow_repo=True)
-                                                       new_got.append(a.cpv)
+                                                       new_got.append(cpv.split(_repo_separator)[0])
                                                got = new_got
                                        if expected:
                                                new_expected = []
@@ -621,13 +603,13 @@ class ResolverPlaygroundTestCase(object):
                                                                if obj[:1] == "!":
                                                                        new_expected.append(obj)
                                                                        continue
-                                                               a = Atom("="+obj, allow_repo=True)
-                                                               new_expected.append(a.cpv)
+                                                               new_expected.append(
+                                                                       obj.split(_repo_separator)[0])
                                                                continue
                                                        new_expected.append(set())
                                                        for cpv in obj:
                                                                if cpv[:1] != "!":
-                                                                       cpv = Atom("="+cpv, allow_repo=True).cpv
+                                                                       cpv = cpv.split(_repo_separator)[0]
                                                                new_expected[-1].add(cpv)
                                                expected = new_expected
                                if self.ignore_mergelist_order and got is not None:
@@ -725,6 +707,7 @@ class ResolverPlaygroundResult(object):
 
                if self.depgraph._dynamic_config._serialized_tasks_cache is not None:
                        self.mergelist = []
+                       host_root = self.depgraph._frozen_config._running_root.root
                        for x in self.depgraph._dynamic_config._serialized_tasks_cache:
                                if isinstance(x, Blocker):
                                        self.mergelist.append(x.atom)
@@ -732,7 +715,16 @@ class ResolverPlaygroundResult(object):
                                        repo_str = ""
                                        if x.metadata["repository"] != "test_repo":
                                                repo_str = _repo_separator + x.metadata["repository"]
-                                       self.mergelist.append(x.cpv + repo_str)
+                                       mergelist_str = x.cpv + repo_str
+                                       if x.built:
+                                               if x.operation == "merge":
+                                                       desc = x.type_name
+                                               else:
+                                                       desc = x.operation
+                                               mergelist_str = "[%s]%s" % (desc, mergelist_str)
+                                       if x.root != host_root:
+                                               mergelist_str += "{targetroot}"
+                                       self.mergelist.append(mergelist_str)
 
                if self.depgraph._dynamic_config._needed_use_config_changes:
                        self.use_changes = {}