1 |
Author: zmedico |
2 |
Date: 2008-08-23 05:43:12 +0000 (Sat, 23 Aug 2008) |
3 |
New Revision: 11455 |
4 |
|
5 |
Modified: |
6 |
main/trunk/pym/portage/dbapi/vartree.py |
7 |
Log: |
8 |
More LinkageMap enhancments from Lucian Poston: |
9 |
* Added _ObjectKey helper class to LinkageMap. (commit eac5528887656abec65fc3a825506187397482e4) |
10 |
* Minor change to docstrings. (commit adde422145d81f25b4024eac1e78b80e1b4a4531) |
11 |
|
12 |
|
13 |
Modified: main/trunk/pym/portage/dbapi/vartree.py |
14 |
=================================================================== |
15 |
--- main/trunk/pym/portage/dbapi/vartree.py 2008-08-23 02:02:06 UTC (rev 11454) |
16 |
+++ main/trunk/pym/portage/dbapi/vartree.py 2008-08-23 05:43:12 UTC (rev 11455) |
17 |
@@ -139,6 +139,9 @@ |
18 |
return rValue |
19 |
|
20 |
class LinkageMap(object): |
21 |
+ |
22 |
+ """Models dynamic linker dependencies.""" |
23 |
+ |
24 |
def __init__(self, vardbapi): |
25 |
self._dbapi = vardbapi |
26 |
self._libs = {} |
27 |
@@ -146,6 +149,61 @@ |
28 |
self._defpath = set(getlibpaths()) |
29 |
self._obj_key_cache = {} |
30 |
|
31 |
+ class _ObjectKey(object): |
32 |
+ |
33 |
+ """Helper class used as _obj_properties keys for objects.""" |
34 |
+ |
35 |
+ def __init__(self, object): |
36 |
+ """ |
37 |
+ This takes a path to an object. |
38 |
+ |
39 |
+ @param object: path to a file |
40 |
+ @type object: string (example: '/usr/bin/bar') |
41 |
+ |
42 |
+ """ |
43 |
+ self._key = self._generate_object_key(object) |
44 |
+ |
45 |
+ def __hash__(self): |
46 |
+ return hash(self._key) |
47 |
+ |
48 |
+ def __eq__(self, other): |
49 |
+ return self._key == other._key |
50 |
+ |
51 |
+ def _generate_object_key(self, object): |
52 |
+ """ |
53 |
+ Generate object key for a given object. |
54 |
+ |
55 |
+ @param object: path to a file |
56 |
+ @type object: string (example: '/usr/bin/bar') |
57 |
+ @rtype: 2-tuple of types (long, int) if object exists. string if |
58 |
+ object does not exist. |
59 |
+ @return: |
60 |
+ 1. 2-tuple of object's inode and device from a stat call, if object |
61 |
+ exists. |
62 |
+ 2. realpath of object if object does not exist. |
63 |
+ |
64 |
+ """ |
65 |
+ try: |
66 |
+ object_stat = os.stat(object) |
67 |
+ except OSError: |
68 |
+ # Use the realpath as the key if the file does not exists on the |
69 |
+ # filesystem. |
70 |
+ return os.path.realpath(object) |
71 |
+ # Return a tuple of the device and inode. |
72 |
+ return (object_stat.st_dev, object_stat.st_ino) |
73 |
+ |
74 |
+ def file_exists(self): |
75 |
+ """ |
76 |
+ Determine if the file for this key exists on the filesystem. |
77 |
+ |
78 |
+ @rtype: Boolean |
79 |
+ @return: |
80 |
+ 1. True if the file exists. |
81 |
+ 2. False if the file does not exist or is a broken symlink. |
82 |
+ |
83 |
+ """ |
84 |
+ return isinstance(self._key, tuple) |
85 |
+ |
86 |
def rebuild(self, include_file=None): |
87 |
libs = {} |
88 |
obj_key_cache = {} |
89 |
@@ -179,7 +237,7 @@ |
90 |
continue |
91 |
arch = fields[0] |
92 |
obj = fields[1] |
93 |
- obj_key = self._generateObjKey(obj) |
94 |
+ obj_key = self._ObjectKey(obj) |
95 |
soname = fields[2] |
96 |
path = set([normalize_path(x) |
97 |
for x in filter(None, fields[3].replace( |
98 |
@@ -206,27 +264,6 @@ |
99 |
self._obj_properties = obj_properties |
100 |
self._obj_key_cache = obj_key_cache |
101 |
|
102 |
- def _generateObjKey(self, obj): |
103 |
- """ |
104 |
- Generate obj key for a given object. |
105 |
- |
106 |
- @param obj: path to an existing file |
107 |
- @type obj: string (example: '/usr/bin/bar') |
108 |
- @rtype: 2-tuple of longs if obj exists. string if obj does not exist. |
109 |
- @return: |
110 |
- 1. 2-tuple of obj's inode and device from a stat call, if obj exists. |
111 |
- 2. realpath of object if obj does not exist. |
112 |
- |
113 |
- """ |
114 |
- try: |
115 |
- obj_st = os.stat(obj) |
116 |
- except OSError: |
117 |
- # Use the realpath as the key if the file does not exists on the |
118 |
- # filesystem. |
119 |
- return os.path.realpath(obj) |
120 |
- # Return a tuple of the device and inode. |
121 |
- return (obj_st.st_dev, obj_st.st_ino) |
122 |
- |
123 |
def listBrokenBinaries(self, debug=False): |
124 |
""" |
125 |
Find binaries and their needed sonames, which have no providers. |
126 |
@@ -239,13 +276,13 @@ |
127 |
object that have no corresponding libraries to fulfill the dependency. |
128 |
|
129 |
""" |
130 |
- class LibraryCache(object): |
131 |
+ class _LibraryCache(object): |
132 |
|
133 |
""" |
134 |
Caches properties associated with paths. |
135 |
|
136 |
- The purpose of this class is to prevent multiple calls of |
137 |
- _generateObjKey on the same paths. |
138 |
+ The purpose of this class is to prevent multiple instances of |
139 |
+ _ObjectKey for the same paths. |
140 |
|
141 |
""" |
142 |
|
143 |
@@ -274,9 +311,9 @@ |
144 |
if obj in self._obj_key_cache: |
145 |
obj_key = self._obj_key_cache.get(obj) |
146 |
else: |
147 |
- obj_key = self._generateObjKey(obj) |
148 |
+ obj_key = self._ObjectKey(obj) |
149 |
# Check that the library exists on the filesystem. |
150 |
- if isinstance(obj_key, tuple): |
151 |
+ if obj_key.file_exists(): |
152 |
# Get the arch and soname from LinkageMap._obj_properties if |
153 |
# it exists. Otherwise, None. |
154 |
arch, _, _, soname, _ = \ |
155 |
@@ -288,7 +325,7 @@ |
156 |
(None, None, obj_key, False)) |
157 |
|
158 |
rValue = {} |
159 |
- cache = LibraryCache() |
160 |
+ cache = _LibraryCache() |
161 |
providers = self.listProviders() |
162 |
|
163 |
# Iterate over all obj_keys and their providers. |
164 |
@@ -372,7 +409,7 @@ |
165 |
self.rebuild() |
166 |
# Iterate over all object keys within LinkageMap. |
167 |
for obj_key in self._obj_properties: |
168 |
- rValue.setdefault(obj_key, self.findProviders(obj_key=obj_key)) |
169 |
+ rValue.setdefault(obj_key, self.findProviders(obj_key)) |
170 |
return rValue |
171 |
|
172 |
def isMasterLink(self, obj): |
173 |
@@ -388,7 +425,7 @@ |
174 |
|
175 |
""" |
176 |
basename = os.path.basename(obj) |
177 |
- obj_key = self._generateObjKey(obj) |
178 |
+ obj_key = self._ObjectKey(obj) |
179 |
if obj_key not in self._obj_properties: |
180 |
raise KeyError("%s (%s) not in object list" % (obj_key, obj)) |
181 |
soname = self._obj_properties[obj_key][3] |
182 |
@@ -429,13 +466,11 @@ |
183 |
raise KeyError("%s not in object list" % obj) |
184 |
return self._obj_properties[self._obj_key_cache[obj]][3] |
185 |
|
186 |
- def findProviders(self, obj=None, obj_key=None): |
187 |
+ def findProviders(self, obj): |
188 |
""" |
189 |
Find providers for an object or object key. |
190 |
|
191 |
- This method should be called with either an obj or obj_key. If called |
192 |
- with both, the obj_key is ignored. If called with neither, KeyError is |
193 |
- raised as if an invalid obj was passed. |
194 |
+ This method may be called with a key from _obj_properties. |
195 |
|
196 |
In some cases, not all valid libraries are returned. This may occur when |
197 |
an soname symlink referencing a library is in an object's runpath while |
198 |
@@ -444,10 +479,8 @@ |
199 |
library dependencies (since the dynamic linker actually searches for |
200 |
files named with the soname in the runpaths). |
201 |
|
202 |
- @param obj: absolute path to an object |
203 |
- @type obj: string (example: '/usr/bin/bar') |
204 |
- @param obj_key: key from LinkageMap._generateObjKey |
205 |
- @type obj_key: 2-tuple of longs or string |
206 |
+ @param obj: absolute path to an object or a key from _obj_properties |
207 |
+ @type obj: string (example: '/usr/bin/bar') or _ObjectKey |
208 |
@rtype: dict (example: {'libbar.so': set(['/lib/libbar.so.1.5'])}) |
209 |
@return: The return value is a soname -> set-of-library-paths, where |
210 |
set-of-library-paths satisfy soname. |
211 |
@@ -459,14 +492,16 @@ |
212 |
self.rebuild() |
213 |
|
214 |
# Determine the obj_key from the arguments. |
215 |
- if obj is not None: |
216 |
+ if isinstance(obj, self._ObjectKey): |
217 |
+ obj_key = obj |
218 |
+ if obj_key not in self._obj_properties: |
219 |
+ raise KeyError("%s not in object list" % obj_key) |
220 |
+ else: |
221 |
obj_key = self._obj_key_cache.get(obj) |
222 |
if obj_key not in self._obj_properties: |
223 |
- obj_key = self._generateObjKey(obj) |
224 |
+ obj_key = self._ObjectKey(obj) |
225 |
if obj_key not in self._obj_properties: |
226 |
raise KeyError("%s (%s) not in object list" % (obj_key, obj)) |
227 |
- elif obj_key not in self._obj_properties: |
228 |
- raise KeyError("%s not in object list" % obj_key) |
229 |
|
230 |
arch, needed, path, _, _ = self._obj_properties[obj_key] |
231 |
path = path.union(self._defpath) |
232 |
@@ -483,23 +518,22 @@ |
233 |
rValue[soname].add(provider) |
234 |
return rValue |
235 |
|
236 |
- def findConsumers(self, obj=None, obj_key=None): |
237 |
+ def findConsumers(self, obj): |
238 |
""" |
239 |
Find consumers of an object or object key. |
240 |
|
241 |
- This method should be called with either an obj or obj_key. If called |
242 |
- with both, the obj_key is ignored. If called with neither, KeyError is |
243 |
- raised as if an invalid obj was passed. |
244 |
+ This method may be called with a key from _obj_properties. If this |
245 |
+ method is going to be called with an object key, to avoid not catching |
246 |
+ shadowed libraries, do not pass new _ObjectKey instances to this method. |
247 |
+ Instead pass the obj as a string. |
248 |
|
249 |
In some cases, not all consumers are returned. This may occur when |
250 |
an soname symlink referencing a library is in an object's runpath while |
251 |
the actual library is not. |
252 |
|
253 |
- @param obj: absolute path to an object |
254 |
- @type obj: string (example: '/usr/bin/bar') |
255 |
- @param obj_key: key from LinkageMap._generateObjKey |
256 |
- @type obj_key: 2-tuple of longs or string |
257 |
- @rtype: set of strings (example: ) |
258 |
+ @param obj: absolute path to an object or a key from _obj_properties |
259 |
+ @type obj: string (example: '/usr/bin/bar') or _ObjectKey |
260 |
+ @rtype: set of strings (example: set(['/bin/foo', '/usr/bin/bar'])) |
261 |
@return: The return value is a soname -> set-of-library-paths, where |
262 |
set-of-library-paths satisfy soname. |
263 |
|
264 |
@@ -510,17 +544,18 @@ |
265 |
self.rebuild() |
266 |
|
267 |
# Determine the obj_key and the set of objects matching the arguments. |
268 |
- if obj is not None: |
269 |
+ if isinstance(obj, self._ObjectKey): |
270 |
+ obj_key = obj |
271 |
+ if obj_key not in self._obj_properties: |
272 |
+ raise KeyError("%s not in object list" % obj_key) |
273 |
+ objs = self._obj_properties[obj_key][4] |
274 |
+ else: |
275 |
objs = set([obj]) |
276 |
obj_key = self._obj_key_cache.get(obj) |
277 |
if obj_key not in self._obj_properties: |
278 |
- obj_key = self._generateObjKey(obj) |
279 |
+ obj_key = self._ObjectKey(obj) |
280 |
if obj_key not in self._obj_properties: |
281 |
raise KeyError("%s (%s) not in object list" % (obj_key, obj)) |
282 |
- else: |
283 |
- if obj_key not in self._obj_properties: |
284 |
- raise KeyError("%s not in object list" % obj_key) |
285 |
- objs = self._obj_properties[obj_key][4] |
286 |
|
287 |
# Determine the directory(ies) from the set of objects. |
288 |
objs_dirs = set([os.path.dirname(x) for x in objs]) |
289 |
@@ -529,7 +564,7 @@ |
290 |
# same soname and the master link points to that |
291 |
# other version, this lib will be shadowed and won't |
292 |
# have any consumers. |
293 |
- if obj is not None: |
294 |
+ if not isinstance(obj, self._ObjectKey): |
295 |
soname = self._obj_properties[obj_key][3] |
296 |
obj_dir = os.path.dirname(obj) |
297 |
master_link = os.path.join(obj_dir, soname) |