More LinkageMap enhancments from Lucian Poston:
authorZac Medico <zmedico@gentoo.org>
Sat, 23 Aug 2008 05:43:12 +0000 (05:43 -0000)
committerZac Medico <zmedico@gentoo.org>
Sat, 23 Aug 2008 05:43:12 +0000 (05:43 -0000)
* Added _ObjectKey helper class to LinkageMap. (commit eac5528887656abec65fc3a825506187397482e4)
* Minor change to docstrings. (commit adde422145d81f25b4024eac1e78b80e1b4a4531)

svn path=/main/trunk/; revision=11455

pym/portage/dbapi/vartree.py

index 3671190d2c75ca5299b0723f9d3cc9ea2f00762a..aa7c9c3fe36d2f66a514e387fee0d979e16efb9a 100644 (file)
@@ -139,6 +139,9 @@ class PreservedLibsRegistry(object):
                return rValue
 
 class LinkageMap(object):
+
+       """Models dynamic linker dependencies."""
+
        def __init__(self, vardbapi):
                self._dbapi = vardbapi
                self._libs = {}
@@ -146,6 +149,61 @@ class LinkageMap(object):
                self._defpath = set(getlibpaths())
                self._obj_key_cache = {}
 
+       class _ObjectKey(object):
+
+               """Helper class used as _obj_properties keys for objects."""
+
+               def __init__(self, object):
+                       """
+                       This takes a path to an object.
+
+                       @param object: path to a file
+                       @type object: string (example: '/usr/bin/bar')
+
+                       """
+                       self._key = self._generate_object_key(object)
+
+               def __hash__(self):
+                       return hash(self._key)
+
+               def __eq__(self, other):
+                       return self._key == other._key
+
+               def _generate_object_key(self, object):
+                       """
+                       Generate object key for a given object.
+
+                       @param object: path to a file
+                       @type object: string (example: '/usr/bin/bar')
+                       @rtype: 2-tuple of types (long, int) if object exists. string if
+                               object does not exist.
+                       @return:
+                               1. 2-tuple of object's inode and device from a stat call, if object
+                                       exists.
+                               2. realpath of object if object does not exist.
+
+                       """
+                       try:
+                               object_stat = os.stat(object)
+                       except OSError:
+                               # Use the realpath as the key if the file does not exists on the
+                               # filesystem.
+                               return os.path.realpath(object)
+                       # Return a tuple of the device and inode.
+                       return (object_stat.st_dev, object_stat.st_ino)
+
+               def file_exists(self):
+                       """
+                       Determine if the file for this key exists on the filesystem.
+
+                       @rtype: Boolean
+                       @return:
+                               1. True if the file exists.
+                               2. False if the file does not exist or is a broken symlink.
+
+                       """
+                       return isinstance(self._key, tuple)
+
        def rebuild(self, include_file=None):
                libs = {}
                obj_key_cache = {}
@@ -179,7 +237,7 @@ class LinkageMap(object):
                                continue
                        arch = fields[0]
                        obj = fields[1]
-                       obj_key = self._generateObjKey(obj)
+                       obj_key = self._ObjectKey(obj)
                        soname = fields[2]
                        path = set([normalize_path(x)
                                for x in filter(None, fields[3].replace(
@@ -206,27 +264,6 @@ class LinkageMap(object):
                self._obj_properties = obj_properties
                self._obj_key_cache = obj_key_cache
 
-       def _generateObjKey(self, obj):
-               """
-               Generate obj key for a given object.
-
-               @param obj: path to an existing file
-               @type obj: string (example: '/usr/bin/bar')
-               @rtype: 2-tuple of longs if obj exists. string if obj does not exist.
-               @return:
-                       1. 2-tuple of obj's inode and device from a stat call, if obj exists.
-                       2. realpath of object if obj does not exist.
-
-               """
-               try:
-                       obj_st = os.stat(obj)
-               except OSError:
-                       # Use the realpath as the key if the file does not exists on the
-                       # filesystem.
-                       return os.path.realpath(obj)
-               # Return a tuple of the device and inode.
-               return (obj_st.st_dev, obj_st.st_ino)
-
        def listBrokenBinaries(self, debug=False):
                """
                Find binaries and their needed sonames, which have no providers.
@@ -239,13 +276,13 @@ class LinkageMap(object):
                        object that have no corresponding libraries to fulfill the dependency.
 
                """
-               class LibraryCache(object):
+               class _LibraryCache(object):
 
                        """
                        Caches properties associated with paths.
 
-                       The purpose of this class is to prevent multiple calls of
-                       _generateObjKey on the same paths.
+                       The purpose of this class is to prevent multiple instances of
+                       _ObjectKey for the same paths.
 
                        """
 
@@ -274,9 +311,9 @@ class LinkageMap(object):
                                        if obj in self._obj_key_cache:
                                                obj_key = self._obj_key_cache.get(obj)
                                        else:
-                                               obj_key = self._generateObjKey(obj)
+                                               obj_key = self._ObjectKey(obj)
                                        # Check that the library exists on the filesystem.
-                                       if isinstance(obj_key, tuple):
+                                       if obj_key.file_exists():
                                                # Get the arch and soname from LinkageMap._obj_properties if
                                                # it exists. Otherwise, None.
                                                arch, _, _, soname, _ = \
@@ -288,7 +325,7 @@ class LinkageMap(object):
                                                                (None, None, obj_key, False))
 
                rValue = {}
-               cache = LibraryCache()
+               cache = _LibraryCache()
                providers = self.listProviders()
 
                # Iterate over all obj_keys and their providers.
@@ -372,7 +409,7 @@ class LinkageMap(object):
                        self.rebuild()
                # Iterate over all object keys within LinkageMap.
                for obj_key in self._obj_properties:
-                       rValue.setdefault(obj_key, self.findProviders(obj_key=obj_key))
+                       rValue.setdefault(obj_key, self.findProviders(obj_key))
                return rValue
 
        def isMasterLink(self, obj):
@@ -388,7 +425,7 @@ class LinkageMap(object):
 
                """
                basename = os.path.basename(obj)
-               obj_key = self._generateObjKey(obj)
+               obj_key = self._ObjectKey(obj)
                if obj_key not in self._obj_properties:
                        raise KeyError("%s (%s) not in object list" % (obj_key, obj))
                soname = self._obj_properties[obj_key][3]
@@ -429,13 +466,11 @@ class LinkageMap(object):
                        raise KeyError("%s not in object list" % obj)
                return self._obj_properties[self._obj_key_cache[obj]][3]
 
-       def findProviders(self, obj=None, obj_key=None):
+       def findProviders(self, obj):
                """
                Find providers for an object or object key.
 
-               This method should be called with either an obj or obj_key.  If called
-               with both, the obj_key is ignored.  If called with neither, KeyError is
-               raised as if an invalid obj was passed.
+               This method may be called with a key from _obj_properties.
 
                In some cases, not all valid libraries are returned.  This may occur when
                an soname symlink referencing a library is in an object's runpath while
@@ -444,10 +479,8 @@ class LinkageMap(object):
                library dependencies (since the dynamic linker actually searches for
                files named with the soname in the runpaths).
 
-               @param obj: absolute path to an object
-               @type obj: string (example: '/usr/bin/bar')
-               @param obj_key: key from LinkageMap._generateObjKey
-               @type obj_key: 2-tuple of longs or string
+               @param obj: absolute path to an object or a key from _obj_properties
+               @type obj: string (example: '/usr/bin/bar') or _ObjectKey
                @rtype: dict (example: {'libbar.so': set(['/lib/libbar.so.1.5'])})
                @return: The return value is a soname -> set-of-library-paths, where
                set-of-library-paths satisfy soname.
@@ -459,14 +492,16 @@ class LinkageMap(object):
                        self.rebuild()
 
                # Determine the obj_key from the arguments.
-               if obj is not None:
+               if isinstance(obj, self._ObjectKey):
+                       obj_key = obj
+                       if obj_key not in self._obj_properties:
+                               raise KeyError("%s not in object list" % obj_key)
+               else:
                        obj_key = self._obj_key_cache.get(obj)
                        if obj_key not in self._obj_properties:
-                               obj_key = self._generateObjKey(obj)
+                               obj_key = self._ObjectKey(obj)
                                if obj_key not in self._obj_properties:
                                        raise KeyError("%s (%s) not in object list" % (obj_key, obj))
-               elif obj_key not in self._obj_properties:
-                       raise KeyError("%s not in object list" % obj_key)
 
                arch, needed, path, _, _ = self._obj_properties[obj_key]
                path = path.union(self._defpath)
@@ -483,23 +518,22 @@ class LinkageMap(object):
                                                rValue[soname].add(provider)
                return rValue
 
-       def findConsumers(self, obj=None, obj_key=None):
+       def findConsumers(self, obj):
                """
                Find consumers of an object or object key.
 
-               This method should be called with either an obj or obj_key.  If called
-               with both, the obj_key is ignored.  If called with neither, KeyError is
-               raised as if an invalid obj was passed.
+               This method may be called with a key from _obj_properties.  If this
+               method is going to be called with an object key, to avoid not catching
+               shadowed libraries, do not pass new _ObjectKey instances to this method.
+               Instead pass the obj as a string.
 
                In some cases, not all consumers are returned.  This may occur when
                an soname symlink referencing a library is in an object's runpath while
                the actual library is not.
 
-               @param obj: absolute path to an object
-               @type obj: string (example: '/usr/bin/bar')
-               @param obj_key: key from LinkageMap._generateObjKey
-               @type obj_key: 2-tuple of longs or string
-               @rtype: set of strings (example: )
+               @param obj: absolute path to an object or a key from _obj_properties
+               @type obj: string (example: '/usr/bin/bar') or _ObjectKey
+               @rtype: set of strings (example: set(['/bin/foo', '/usr/bin/bar']))
                @return: The return value is a soname -> set-of-library-paths, where
                set-of-library-paths satisfy soname.
 
@@ -510,17 +544,18 @@ class LinkageMap(object):
                        self.rebuild()
 
                # Determine the obj_key and the set of objects matching the arguments.
-               if obj is not None:
+               if isinstance(obj, self._ObjectKey):
+                       obj_key = obj
+                       if obj_key not in self._obj_properties:
+                               raise KeyError("%s not in object list" % obj_key)
+                       objs = self._obj_properties[obj_key][4]
+               else:
                        objs = set([obj])
                        obj_key = self._obj_key_cache.get(obj)
                        if obj_key not in self._obj_properties:
-                               obj_key = self._generateObjKey(obj)
+                               obj_key = self._ObjectKey(obj)
                                if obj_key not in self._obj_properties:
                                        raise KeyError("%s (%s) not in object list" % (obj_key, obj))
-               else:
-                       if obj_key not in self._obj_properties:
-                               raise KeyError("%s not in object list" % obj_key)
-                       objs = self._obj_properties[obj_key][4]
 
                # Determine the directory(ies) from the set of objects.
                objs_dirs = set([os.path.dirname(x) for x in objs])
@@ -529,7 +564,7 @@ class LinkageMap(object):
                # same soname and the master link points to that
                # other version, this lib will be shadowed and won't
                # have any consumers.
-               if obj is not None:
+               if not isinstance(obj, self._ObjectKey):
                        soname = self._obj_properties[obj_key][3]
                        obj_dir = os.path.dirname(obj)
                        master_link = os.path.join(obj_dir, soname)