Add experimental EPREFIX/EROOT support to the config and vartree
authorZac Medico <zmedico@gentoo.org>
Fri, 20 Aug 2010 04:28:10 +0000 (21:28 -0700)
committerZac Medico <zmedico@gentoo.org>
Fri, 20 Aug 2010 04:28:10 +0000 (21:28 -0700)
classes, and use it in ResolverPlayground to emulate a prefix-like
environment. This fixes ResolverPlayground so that it doesn't have
to abuse the --root and --root-deps options in order to create a
testing environment. Instead it simply creates a temporary EPREFIX.
WARNING: EPREFIX/EROOT support is experimental and may be incomplete
for cases in which EPREFIX is non-empty.

pym/_emerge/depgraph.py
pym/portage/dbapi/porttree.py
pym/portage/dbapi/vartree.py
pym/portage/package/ebuild/config.py
pym/portage/tests/resolver/ResolverPlayground.py

index ed64e49e016256a2ec46b43a19126ee41d9de066..704686567b980e8ad3305a6ce67dd925df2fedd4 100644 (file)
@@ -72,10 +72,7 @@ class _frozen_depgraph_config(object):
                if settings.get("PORTAGE_DEBUG", "") == "1":
                        self.edebug = 1
                self.spinner = spinner
-               if "_test_" in myopts and "/" not in trees:
-                       self._running_root = trees[self.target_root]["root_config"]
-               else:
-                       self._running_root = trees["/"]["root_config"]
+               self._running_root = trees["/"]["root_config"]
                self._opts_no_restart = frozenset(["--buildpkgonly",
                        "--fetchonly", "--fetch-all-uri", "--pretend"])
                self.pkgsettings = {}
index 043adc642f26bb03ed2b88a7efd0d8333b4e17b9..c925d9bf8ec92d6d8b4daa0a8b92795b536b2b1c 100644 (file)
@@ -1056,7 +1056,7 @@ def close_portdbapi_caches():
                i.close_caches()
 
 class portagetree(object):
-       def __init__(self, root="/", virtual=None, settings=None):
+       def __init__(self, root=None, virtual=None, settings=None):
                """
                Constructor for a PortageTree
                
@@ -1068,14 +1068,21 @@ class portagetree(object):
                @type settings: Instance of portage.config
                """
 
-               if True:
-                       self.root = root
-                       if settings is None:
-                               from portage import settings
-                       self.settings = settings
-                       self.portroot = settings["PORTDIR"]
-                       self.virtual = virtual
-                       self.dbapi = portdbapi(mysettings=settings)
+               if settings is None:
+                       settings = portage.settings
+               self.settings = settings
+
+               self.root = settings['ROOT']
+               if root is not None and root != self.root:
+                       warnings.warn("The root parameter of the " + \
+                               "portage.dbapi.porttree.portagetree" + \
+                               " constructor is now unused. Use " + \
+                               "settings['ROOT'] instead.",
+                               DeprecationWarning, stacklevel=2)
+
+               self.portroot = settings["PORTDIR"]
+               self.virtual = virtual
+               self.dbapi = portdbapi(mysettings=settings)
 
        def dep_bestmatch(self,mydep):
                "compatibility method"
index 80057e2dd5ca4759a0ca2de169ace7d6ee9c638f..c406f5b480abd91a4c7b35ba0e4a28c64e416828 100644 (file)
@@ -61,6 +61,7 @@ import stat
 import sys
 import tempfile
 import time
+import warnings
 
 try:
        import cPickle as pickle
@@ -815,16 +816,12 @@ class vardbapi(dbapi):
        _aux_cache_keys_re = re.compile(r'^NEEDED\..*$')
        _aux_multi_line_re = re.compile(r'^(CONTENTS|NEEDED\..*)$')
 
-       def __init__(self, root, categories=None, settings=None, vartree=None):
+       def __init__(self, _unused_param=None, categories=None, settings=None, vartree=None):
                """
                The categories parameter is unused since the dbapi class
                now has a categories property that is generated from the
                available packages.
                """
-               self.root = _unicode_decode(root,
-                       encoding=_encodings['content'], errors='strict')
-               if self.root[-1] != '/':
-                       self.root += '/'
 
                # Used by emerge to check whether any packages
                # have been added or removed.
@@ -841,11 +838,20 @@ class vardbapi(dbapi):
 
                self.blockers = None
                if settings is None:
-                       from portage import settings
+                       settings = portage.settings
                self.settings = settings
+               self.root = settings['ROOT']
+
+               if _unused_param is not None and _unused_param != self.root:
+                       warnings.warn("The first parameter of the " + \
+                               "portage.dbapi.vartree.vardbapi" + \
+                               " constructor is now unused. Use " + \
+                               "settings['ROOT'] instead.",
+                               DeprecationWarning, stacklevel=2)
+
+               self._eroot = settings['EROOT']
                if vartree is None:
-                       from portage import db
-                       vartree = db[root]["vartree"]
+                       vartree = portage.db[self.root]["vartree"]
                self.vartree = vartree
                self._aux_cache_keys = set(
                        ["BUILD_TIME", "CHOST", "COUNTER", "DEPEND", "DESCRIPTION",
@@ -854,14 +860,14 @@ class vardbapi(dbapi):
                        "repository", "RESTRICT" , "SLOT", "USE", "DEFINED_PHASES",
                        "REQUIRED_USE"])
                self._aux_cache_obj = None
-               self._aux_cache_filename = os.path.join(self.root,
+               self._aux_cache_filename = os.path.join(self._eroot,
                        CACHE_PATH, "vdb_metadata.pickle")
-               self._counter_path = os.path.join(root,
+               self._counter_path = os.path.join(self._eroot,
                        CACHE_PATH, "counter")
 
                try:
-                       self.plib_registry = PreservedLibsRegistry(self.root,
-                               os.path.join(self.root, PRIVATE_PATH, "preserved_libs_registry"))
+                       self.plib_registry = PreservedLibsRegistry(self._eroot,
+                               os.path.join(self._eroot, PRIVATE_PATH, "preserved_libs_registry"))
                except PermissionDenied:
                        # apparently this user isn't allowed to access PRIVATE_PATH
                        self.plib_registry = None
@@ -872,7 +878,7 @@ class vardbapi(dbapi):
        def getpath(self, mykey, filename=None):
                # This is an optimized hotspot, so don't use unicode-wrapped
                # os module and don't use os.path.join().
-               rValue = self.root + VDB_PATH + _os.sep + mykey
+               rValue = self._eroot + VDB_PATH + _os.sep + mykey
                if filename is not None:
                        # If filename is always relative, we can do just
                        # rValue += _os.sep + filename
@@ -884,7 +890,7 @@ class vardbapi(dbapi):
                This is called before an after any modifications, so that consumers
                can use directory mtimes to validate caches. See bug #290428.
                """
-               base = self.root + VDB_PATH
+               base = self._eroot + VDB_PATH
                cat = catsplit(cpv)[0]
                catdir = base + _os.sep + cat
                t = time.time()
@@ -1025,7 +1031,7 @@ class vardbapi(dbapi):
                involve merge or unmerge of packages).
                """
                returnme = []
-               basepath = os.path.join(self.root, VDB_PATH) + os.path.sep
+               basepath = os.path.join(self._eroot, VDB_PATH) + os.path.sep
 
                if use_cache:
                        from portage import listdir
@@ -1117,7 +1123,7 @@ class vardbapi(dbapi):
                        return list(self._iter_match(mydep,
                                self.cp_list(mydep.cp, use_cache=use_cache)))
                try:
-                       curmtime = os.stat(os.path.join(self.root, VDB_PATH, mycat)).st_mtime
+                       curmtime = os.stat(os.path.join(self._eroot, VDB_PATH, mycat)).st_mtime
                except (IOError, OSError):
                        curmtime=0
 
@@ -1760,26 +1766,23 @@ class vardbapi(dbapi):
 
 class vartree(object):
        "this tree will scan a var/db/pkg database located at root (passed to init)"
-       def __init__(self, root="/", virtual=None, clone=None, categories=None,
+       def __init__(self, root=None, virtual=None, clone=None, categories=None,
                settings=None):
-               if clone:
-                       writemsg("vartree.__init__(): deprecated " + \
-                               "use of clone parameter\n", noiselevel=-1)
-                       self.root = clone.root[:]
-                       self.dbapi = copy.deepcopy(clone.dbapi)
-                       self.populated = 1
-                       from portage import config
-                       self.settings = config(clone=clone.settings)
-               else:
-                       self.root = root[:]
-                       if settings is None:
-                               from portage import settings
-                       self.settings = settings
-                       if categories is None:
-                               categories = settings.categories
-                       self.dbapi = vardbapi(self.root, categories=categories,
-                               settings=settings, vartree=self)
-                       self.populated = 1
+
+               if settings is None:
+                       settings = portage.settings
+               self.root = settings['ROOT']
+
+               if root is not None and root != self.root:
+                       warnings.warn("The 'root' parameter of the " + \
+                               "portage.dbapi.vartree.vartree" + \
+                               " constructor is now unused. Use " + \
+                               "settings['ROOT'] instead.",
+                               DeprecationWarning, stacklevel=2)
+
+               self.settings = settings
+               self.dbapi = vardbapi(settings=settings, vartree=self)
+               self.populated = 1
 
        def getpath(self, mykey, filename=None):
                return self.dbapi.getpath(mykey, filename=filename)
@@ -1807,7 +1810,7 @@ class vartree(object):
                except SystemExit as e:
                        raise
                except Exception as e:
-                       mydir = os.path.join(self.root, VDB_PATH, mycpv)
+                       mydir = os.path.join(self._eroot, VDB_PATH, mycpv)
                        writemsg(_("\nParse Error reading PROVIDE and USE in '%s'\n") % mydir,
                                noiselevel=-1)
                        if mylines:
@@ -1926,7 +1929,10 @@ class dblink(object):
                self._blockers = blockers
                self._scheduler = scheduler
 
-               self.dbroot = normalize_path(os.path.join(myroot, VDB_PATH))
+               # WARNING: EROOT support is experimental and may be incomplete
+               # for cases in which EPREFIX is non-empty.
+               self._eroot = mysettings['EROOT']
+               self.dbroot = normalize_path(os.path.join(self._eroot, VDB_PATH))
                self.dbcatdir = self.dbroot+"/"+cat
                self.dbpkgdir = self.dbcatdir+"/"+pkg
                self.dbtmpdir = self.dbcatdir+"/-MERGING-"+pkg
@@ -2039,7 +2045,7 @@ class dblink(object):
                obj_index = contents_re.groupindex['obj']
                dir_index = contents_re.groupindex['dir']
                sym_index = contents_re.groupindex['sym']
-               myroot = self.myroot
+               myroot = self._eroot
                if myroot == os.path.sep:
                        myroot = None
                pos = 0
@@ -2666,7 +2672,7 @@ class dblink(object):
                #remove self from vartree database so that our own virtual gets zapped if we're the last node
                self.vartree.zap(self.mycpv)
 
-       def isowner(self, filename, destroot):
+       def isowner(self, filename, destroot=None):
                """ 
                Check if a file belongs to this package. This may
                result in a stat call for the parent directory of
@@ -2685,9 +2691,17 @@ class dblink(object):
                1. True if this package owns the file.
                2. False if this package does not own the file.
                """
-               return bool(self._match_contents(filename, destroot))
 
-       def _match_contents(self, filename, destroot):
+               if destroot is not None and destroot != self._eroot:
+                       warnings.warn("The second parameter of the " + \
+                               "portage.dbapi.vartree.dblink.isowner()" + \
+                               " is now unused. Instead " + \
+                               "self.settings['EROOT'] will be used.",
+                               DeprecationWarning, stacklevel=2)
+
+               return bool(self._match_contents(filename))
+
+       def _match_contents(self, filename, destroot=None):
                """
                The matching contents entry is returned, which is useful
                since the path may differ from the one given by the caller,
@@ -2701,8 +2715,14 @@ class dblink(object):
                filename = _unicode_decode(filename,
                        encoding=_encodings['content'], errors='strict')
 
-               destroot = _unicode_decode(destroot,
-                       encoding=_encodings['content'], errors='strict')
+               if destroot is not None and destroot != self._eroot:
+                       warnings.warn("The second parameter of the " + \
+                               "portage.dbapi.vartree.dblink._match_contents()" + \
+                               " is now unused. Instead " + \
+                               "self.settings['EROOT'] will be used.",
+                               DeprecationWarning, stacklevel=2)
+
+               destroot = self._eroot
 
                # The given filename argument might have a different encoding than the
                # the filenames contained in the contents, so use separate wrapped os
index 4893cfbb4a6f341cdfe0b52a15897208e00664b9..46cac3b497210aacb1d5e6ec1220312d199a24ed 100644 (file)
@@ -317,7 +317,7 @@ class config(object):
 
        def __init__(self, clone=None, mycpv=None, config_profile_path=None,
                config_incrementals=None, config_root=None, target_root=None,
-               local_config=True, env=None):
+               _eprefix=None, local_config=True, env=None):
                """
                @param clone: If provided, init will use deepcopy to copy by value the instance.
                @type clone: Instance of config class.
@@ -333,6 +333,8 @@ class config(object):
                @type config_root: String
                @param target_root: __init__ override of $ROOT env variable.
                @type target_root: String
+               @param _eprefix: set the EPREFIX variable (private, used by internal tests)
+               @type _eprefix: String
                @param local_config: Enables loading of local config (/etc/portage); used most by repoman to
                ignore local config (keywording and unmasking)
                @type local_config: Boolean
@@ -341,6 +343,10 @@ class config(object):
                @type env: dict
                """
 
+               # rename local _eprefix variable for convenience
+               eprefix = _eprefix
+               del _eprefix
+
                # When initializing the global portage.settings instance, avoid
                # raising exceptions whenever possible since exceptions thrown
                # from 'import portage' or 'import portage.exceptions' statements
@@ -458,8 +464,10 @@ class config(object):
                                                noiselevel=-1)
                                        raise DirectoryNotFound(var)
 
+                       if eprefix is None:
+                               eprefix = ""
                        if config_root is None:
-                               config_root = "/"
+                               config_root = eprefix + os.sep
 
                        config_root = normalize_path(os.path.abspath(
                                config_root)).rstrip(os.path.sep) + os.path.sep
@@ -664,6 +672,8 @@ class config(object):
                        ensure_dirs(target_root)
                        check_var_directory("ROOT", target_root)
 
+                       eroot = target_root.rstrip(os.sep) + eprefix + os.sep
+
                        # The expand_map is used for variable substitution
                        # in getconfig() calls, and the getconfig() calls
                        # update expand_map with the value of each variable
@@ -681,7 +691,7 @@ class config(object):
                        # lead to unexpected results.
                        expand_map = {}
 
-                       env_d = getconfig(os.path.join(target_root, "etc", "profile.env"),
+                       env_d = getconfig(os.path.join(eroot, "etc", "profile.env"),
                                expand=expand_map)
                        # env_d will be None if profile.env doesn't exist.
                        if env_d:
@@ -798,10 +808,9 @@ class config(object):
                        self["ROOT"] = target_root
                        self.backup_changes("ROOT")
 
-                       # Prefix forward compatability, set EPREFIX to the empty string
-                       self["EPREFIX"] = ''
+                       self["EPREFIX"] = eprefix
                        self.backup_changes("EPREFIX")
-                       self["EROOT"] = target_root
+                       self["EROOT"] = eroot
                        self.backup_changes("EROOT")
 
                        self.pusedict = portage.dep.ExtendedAtomDict(dict)
@@ -1078,7 +1087,7 @@ class config(object):
                """
                Create a few directories that are critical to portage operation
                """
-               if not os.access(self["ROOT"], os.W_OK):
+               if not os.access(self["EROOT"], os.W_OK):
                        return
 
                #                                gid, mode, mask, preserve_perms
@@ -1091,7 +1100,7 @@ class config(object):
 
                for mypath, (gid, mode, modemask, preserve_perms) \
                        in dir_mode_map.items():
-                       mydir = os.path.join(self["ROOT"], mypath)
+                       mydir = os.path.join(self["EROOT"], mypath)
                        if preserve_perms and os.path.isdir(mydir):
                                # Only adjust permissions on some directories if
                                # they don't exist yet. This gives freedom to the
@@ -2185,7 +2194,7 @@ class config(object):
 
        def reload(self):
                """Reload things like /etc/profile.env that can change during runtime."""
-               env_d_filename = os.path.join(self["ROOT"], "etc", "profile.env")
+               env_d_filename = os.path.join(self["EROOT"], "etc", "profile.env")
                self.configdict["env.d"].clear()
                env_d = getconfig(env_d_filename, expand=False)
                if env_d:
index fb691ba638e6fcd0a7af7705cd29bc3632870623..1ce371d9b5d6597638208868c5c8551e28ef3548 100644 (file)
@@ -35,9 +35,11 @@ class ResolverPlayground(object):
                profile: settings defined by the profile.
                """
                self.debug = debug
-               self.root = tempfile.mkdtemp() + os.path.sep
-               self.portdir = os.path.join(self.root, "usr/portage")
-               self.vdbdir = os.path.join(self.root, "var/db/pkg")
+               self.root = "/"
+               self.eprefix = tempfile.mkdtemp()
+               self.eroot = self.root + self.eprefix.lstrip(os.sep) + os.sep
+               self.portdir = os.path.join(self.eroot, "usr/portage")
+               self.vdbdir = os.path.join(self.eroot, "var/db/pkg")
                os.makedirs(self.portdir)
                os.makedirs(self.vdbdir)
                
@@ -182,12 +184,12 @@ class ResolverPlayground(object):
                        raise NotImplentedError()
                
                #Create profile symlink
-               os.makedirs(os.path.join(self.root, "etc"))
-               os.symlink(sub_profile_dir, os.path.join(self.root, "etc", "make.profile"))
+               os.makedirs(os.path.join(self.eroot, "etc"))
+               os.symlink(sub_profile_dir, os.path.join(self.eroot, "etc", "make.profile"))
 
        def _create_world(self, world):
                #Create /var/lib/portage/world
-               var_lib_portage = os.path.join(self.root, "var", "lib", "portage")
+               var_lib_portage = os.path.join(self.eroot, "var", "lib", "portage")
                os.makedirs(var_lib_portage)
 
                world_file = os.path.join(var_lib_portage, "world")
@@ -201,8 +203,7 @@ class ResolverPlayground(object):
                env = {
                        "ACCEPT_KEYWORDS": "x86",
                        "PORTDIR": self.portdir,
-                       "ROOT": self.root,
-                       'PORTAGE_TMPDIR'       : os.path.join(self.root, 'var/tmp'),
+                       'PORTAGE_TMPDIR'       : os.path.join(self.eroot, 'var/tmp'),
                }
 
                # Pass along PORTAGE_USERNAME and PORTAGE_GRPNAME since they
@@ -212,15 +213,16 @@ class ResolverPlayground(object):
                if 'PORTAGE_GRPNAME' in os.environ:
                        env['PORTAGE_GRPNAME'] = os.environ['PORTAGE_GRPNAME']
 
-               settings = config(config_root=self.root, target_root=self.root, env=env)
+               settings = config(_eprefix=self.eprefix, env=env)
                settings.lock()
 
                trees = {
                        self.root: {
-                                       "virtuals": settings.getvirtuals(),
-                                       "vartree": vartree(self.root, categories=settings.categories, settings=settings),
+                                       "vartree": vartree(settings=settings),
                                        "porttree": portagetree(self.root, settings=settings),
-                                       "bintree": binarytree(self.root, os.path.join(self.root, "usr/portage/packages"), settings=settings)
+                                       "bintree": binarytree(self.root,
+                                               os.path.join(self.eroot, "usr/portage/packages"),
+                                               settings=settings)
                                }
                        }
 
@@ -237,14 +239,8 @@ class ResolverPlayground(object):
                options = options.copy()
                options["--pretend"] = True
                options["--quiet"] = True
-               options["--root"] = self.root
-               options["--config-root"] = self.root
-               options["--root-deps"] = True
                if self.debug:
                        options["--debug"] = True
-               # Add a fake _test_ option that can be used for
-               # conditional test code.
-               options["_test_"] = True
 
                if not self.debug:
                        portage.util.noiselimit = -2
@@ -267,9 +263,9 @@ class ResolverPlayground(object):
 
        def cleanup(self):
                if self.debug:
-                       print("\nROOT=%s" % self.root)
+                       print("\nEROOT=%s" % self.eroot)
                else:
-                       shutil.rmtree(self.root)
+                       shutil.rmtree(self.eroot)
 
 class ResolverPlaygroundTestCase(object):