From c0ee69585bf87e09c237668531127e79e0cd1c46 Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Thu, 19 Aug 2010 21:28:10 -0700 Subject: [PATCH] Add experimental EPREFIX/EROOT support to the config and vartree 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 | 5 +- pym/portage/dbapi/porttree.py | 25 +++-- pym/portage/dbapi/vartree.py | 106 +++++++++++------- pym/portage/package/ebuild/config.py | 27 +++-- .../tests/resolver/ResolverPlayground.py | 36 +++--- 5 files changed, 114 insertions(+), 85 deletions(-) diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py index ed64e49e0..704686567 100644 --- a/pym/_emerge/depgraph.py +++ b/pym/_emerge/depgraph.py @@ -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 = {} diff --git a/pym/portage/dbapi/porttree.py b/pym/portage/dbapi/porttree.py index 043adc642..c925d9bf8 100644 --- a/pym/portage/dbapi/porttree.py +++ b/pym/portage/dbapi/porttree.py @@ -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" diff --git a/pym/portage/dbapi/vartree.py b/pym/portage/dbapi/vartree.py index 80057e2dd..c406f5b48 100644 --- a/pym/portage/dbapi/vartree.py +++ b/pym/portage/dbapi/vartree.py @@ -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 diff --git a/pym/portage/package/ebuild/config.py b/pym/portage/package/ebuild/config.py index 4893cfbb4..46cac3b49 100644 --- a/pym/portage/package/ebuild/config.py +++ b/pym/portage/package/ebuild/config.py @@ -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: diff --git a/pym/portage/tests/resolver/ResolverPlayground.py b/pym/portage/tests/resolver/ResolverPlayground.py index fb691ba63..1ce371d9b 100644 --- a/pym/portage/tests/resolver/ResolverPlayground.py +++ b/pym/portage/tests/resolver/ResolverPlayground.py @@ -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): -- 2.26.2