1 |
Author: genone |
2 |
Date: 2008-05-02 08:20:39 +0000 (Fri, 02 May 2008) |
3 |
New Revision: 10081 |
4 |
|
5 |
Added: |
6 |
main/trunk/pym/portage/sets/libs.py |
7 |
Modified: |
8 |
main/trunk/cnf/sets.conf |
9 |
main/trunk/pym/_emerge/__init__.py |
10 |
main/trunk/pym/portage/dbapi/vartree.py |
11 |
main/trunk/pym/portage/sets/dbapi.py |
12 |
Log: |
13 |
v2 of FEATURES=preserved-libs, using LinkageMap instead of the now removed LibraryPackageMap class |
14 |
|
15 |
Modified: main/trunk/cnf/sets.conf |
16 |
=================================================================== |
17 |
--- main/trunk/cnf/sets.conf 2008-05-02 05:09:48 UTC (rev 10080) |
18 |
+++ main/trunk/cnf/sets.conf 2008-05-02 08:20:39 UTC (rev 10081) |
19 |
@@ -39,5 +39,5 @@ |
20 |
# Set to rebuild all packages that need a preserved lib that only remains due |
21 |
# to FEATURES=preserve-libs |
22 |
[preserved-rebuild] |
23 |
-class = portage.sets.dbapi.PreservedLibraryConsumerSet |
24 |
+class = portage.sets.libs.PreservedLibraryConsumerSet |
25 |
world-candidate = False |
26 |
|
27 |
Modified: main/trunk/pym/_emerge/__init__.py |
28 |
=================================================================== |
29 |
--- main/trunk/pym/_emerge/__init__.py 2008-05-02 05:09:48 UTC (rev 10080) |
30 |
+++ main/trunk/pym/_emerge/__init__.py 2008-05-02 08:20:39 UTC (rev 10081) |
31 |
@@ -7878,6 +7878,7 @@ |
32 |
portage.writemsg_stdout(colorize("WARN", "WARNING:") |
33 |
+ " AUTOCLEAN is disabled. This can cause serious" |
34 |
+ " problems due to overlapping packages.\n") |
35 |
+ trees[settings["ROOT"]]["vartree"].dbapi.plib_registry.pruneNonExisting() |
36 |
|
37 |
if merge_count and not (buildpkgonly or fetchonly or pretend): |
38 |
post_emerge(trees, mtimedb, retval) |
39 |
|
40 |
Modified: main/trunk/pym/portage/dbapi/vartree.py |
41 |
=================================================================== |
42 |
--- main/trunk/pym/portage/dbapi/vartree.py 2008-05-02 05:09:48 UTC (rev 10080) |
43 |
+++ main/trunk/pym/portage/dbapi/vartree.py 2008-05-02 08:20:39 UTC (rev 10081) |
44 |
@@ -27,7 +27,7 @@ |
45 |
from portage.elog.messages import ewarn |
46 |
from portage.elog.filtering import filter_mergephases, filter_unmergephases |
47 |
|
48 |
-import os, re, sys, stat, errno, commands, copy, time |
49 |
+import os, re, sys, stat, errno, commands, copy, time, subprocess |
50 |
from itertools import izip |
51 |
|
52 |
try: |
53 |
@@ -135,38 +135,63 @@ |
54 |
def rebuild(self): |
55 |
libs = {} |
56 |
obj_properties = {} |
57 |
+ lines = [] |
58 |
for cpv in self._dbapi.cpv_all(): |
59 |
- lines = grabfile(self._dbapi.getpath(cpv, filename="NEEDED.2")) |
60 |
- for l in lines: |
61 |
- fields = l.strip("\n").split(";") |
62 |
- if len(fields) < 5: |
63 |
- print "Error", fields |
64 |
- # insufficient field length |
65 |
- continue |
66 |
- arch = fields[0] |
67 |
- obj = fields[1] |
68 |
- soname = fields[2] |
69 |
- path = fields[3].replace("${ORIGIN}", os.path.dirname(obj)).replace("$ORIGIN", os.path.dirname(obj)).split(":") |
70 |
- needed = fields[4].split(",") |
71 |
- if soname: |
72 |
- libs.setdefault(soname, {arch: {"providers": [], "consumers": []}}) |
73 |
- libs[soname].setdefault(arch, {"providers": [], "consumers": []}) |
74 |
- libs[soname][arch]["providers"].append(obj) |
75 |
- for x in needed: |
76 |
- libs.setdefault(x, {arch: {"providers": [], "consumers": []}}) |
77 |
- libs[x].setdefault(arch, {"providers": [], "consumers": []}) |
78 |
- libs[x][arch]["consumers"].append(obj) |
79 |
- obj_properties[obj] = (arch, path, needed, soname) |
80 |
+ lines += grabfile(self._dbapi.getpath(cpv, filename="NEEDED.2")) |
81 |
+ |
82 |
+ # have to call scanelf for preserved libs here as they aren't |
83 |
+ # registered in NEEDED.2 files |
84 |
+ if self._dbapi.plib_registry and self._dbapi.plib_registry.getPreservedLibs(): |
85 |
+ args = ["/usr/bin/scanelf", "-yqF", "%a;%F;%S;%r;%n"] |
86 |
+ for items in self._dbapi.plib_registry.getPreservedLibs().values(): |
87 |
+ args += items |
88 |
+ proc = subprocess.Popen(args, stdout=subprocess.PIPE) |
89 |
+ output = [l[3:] for l in proc.communicate()[0].split("\n")] |
90 |
+ lines += output |
91 |
+ |
92 |
+ for l in lines: |
93 |
+ if l.strip() == "": |
94 |
+ continue |
95 |
+ fields = l.strip("\n").split(";") |
96 |
+ if len(fields) < 5: |
97 |
+ print "Error", fields |
98 |
+ # insufficient field length |
99 |
+ continue |
100 |
+ arch = fields[0] |
101 |
+ obj = fields[1] |
102 |
+ soname = fields[2] |
103 |
+ path = fields[3].replace("${ORIGIN}", os.path.dirname(obj)).replace("$ORIGIN", os.path.dirname(obj)).split(":") |
104 |
+ needed = fields[4].split(",") |
105 |
+ if soname: |
106 |
+ libs.setdefault(soname, {arch: {"providers": [], "consumers": []}}) |
107 |
+ libs[soname].setdefault(arch, {"providers": [], "consumers": []}) |
108 |
+ libs[soname][arch]["providers"].append(obj) |
109 |
+ for x in needed: |
110 |
+ libs.setdefault(x, {arch: {"providers": [], "consumers": []}}) |
111 |
+ libs[x].setdefault(arch, {"providers": [], "consumers": []}) |
112 |
+ libs[x][arch]["consumers"].append(obj) |
113 |
+ obj_properties[obj] = (arch, needed, path, soname) |
114 |
|
115 |
self._libs = libs |
116 |
self._obj_properties = obj_properties |
117 |
+ |
118 |
+ def listLibraryObjects(self): |
119 |
+ rValue = [] |
120 |
+ if not self._libs: |
121 |
+ self.rebuild() |
122 |
+ for soname in self._libs: |
123 |
+ for arch in self._libs[soname]: |
124 |
+ rValue.extend(self._libs[soname][arch]["providers"]) |
125 |
+ return rValue |
126 |
|
127 |
def findProviders(self, obj): |
128 |
+ if not self._libs: |
129 |
+ self.rebuild() |
130 |
obj = os.path.realpath(obj) |
131 |
rValue = {} |
132 |
if obj not in self._obj_properties: |
133 |
raise KeyError("%s not in object list" % obj) |
134 |
- arch, path, needed, soname = self._obj_properties[obj] |
135 |
+ arch, needed, path, soname = self._obj_properties[obj] |
136 |
path.extend(self._defpath) |
137 |
path = [os.path.realpath(x) for x in path] |
138 |
for x in needed: |
139 |
@@ -181,13 +206,15 @@ |
140 |
return rValue |
141 |
|
142 |
def findConsumers(self, obj): |
143 |
+ if not self._libs: |
144 |
+ self.rebuild() |
145 |
obj = os.path.realpath(obj) |
146 |
rValue = set() |
147 |
for soname in self._libs: |
148 |
for arch in self._libs[soname]: |
149 |
if obj in self._libs[soname][arch]["providers"]: |
150 |
for x in self._libs[soname][arch]["consumers"]: |
151 |
- path = self._obj_properties[x][1] |
152 |
+ path = self._obj_properties[x][2] |
153 |
path = [os.path.realpath(y) for y in path+self._defpath] |
154 |
if soname[0] == os.sep and os.path.realpath(soname) == os.path.realpath(obj): |
155 |
rValue.add(x) |
156 |
@@ -275,7 +302,6 @@ |
157 |
self._counter_path = os.path.join(root, |
158 |
CACHE_PATH.lstrip(os.path.sep), "counter") |
159 |
|
160 |
- self.libmap = LibraryPackageMap(os.path.join(self.root, CACHE_PATH.lstrip(os.sep), "library_consumers"), self) |
161 |
try: |
162 |
self.plib_registry = PreservedLibsRegistry( |
163 |
os.path.join(self.root, PRIVATE_PATH, "preserved_libs_registry")) |
164 |
@@ -283,6 +309,8 @@ |
165 |
# apparently this user isn't allowed to access PRIVATE_PATH |
166 |
self.plib_registry = None |
167 |
|
168 |
+ self.linkmap = LinkageMap(self) |
169 |
+ |
170 |
def getpath(self, mykey, filename=None): |
171 |
rValue = os.path.join(self.root, VDB_PATH, mykey) |
172 |
if filename != None: |
173 |
@@ -1253,31 +1281,18 @@ |
174 |
return retval |
175 |
|
176 |
# regenerate reverse NEEDED map |
177 |
- self.vartree.dbapi.libmap.update() |
178 |
+ self.vartree.dbapi.linkmap.rebuild() |
179 |
|
180 |
# remove preserved libraries that don't have any consumers left |
181 |
# FIXME: this code is quite ugly and can likely be optimized in several ways |
182 |
plib_dict = plib_registry.getPreservedLibs() |
183 |
for cpv in plib_dict: |
184 |
- keeplist = [] |
185 |
plib_dict[cpv].sort() |
186 |
for f in plib_dict[cpv]: |
187 |
- if not os.path.exists(f) or os.path.realpath(f) in keeplist: |
188 |
+ if not os.path.exists(f): |
189 |
continue |
190 |
unlink_list = [] |
191 |
- while os.path.islink(f): |
192 |
- if os.path.basename(f) in self.vartree.dbapi.libmap.get(): |
193 |
- unlink_list = [] |
194 |
- keeplist.append(os.path.realpath(f)) |
195 |
- break |
196 |
- else: |
197 |
- unlink_list.append(f) |
198 |
- # only follow symlinks if the target is also a preserved lib object |
199 |
- if os.readlink(f) in plib_dict[cpv]: |
200 |
- f = os.readlink(f) |
201 |
- else: |
202 |
- break |
203 |
- if not os.path.islink(f) and not os.path.basename(f) in self.vartree.dbapi.libmap.get(): |
204 |
+ if not self.vartree.dbapi.linkmap.findConsumers(f): |
205 |
unlink_list.append(f) |
206 |
for obj in unlink_list: |
207 |
try: |
208 |
@@ -1664,22 +1679,27 @@ |
209 |
|
210 |
def _preserve_libs(self, srcroot, destroot, mycontents, counter): |
211 |
# read global reverse NEEDED map |
212 |
- libmap = self.vartree.dbapi.libmap.get() |
213 |
+ linkmap = self.vartree.dbapi.linkmap |
214 |
+ linkmap.rebuild() |
215 |
+ liblist = linkmap.listLibraryObjects() |
216 |
|
217 |
# get list of libraries from old package instance |
218 |
old_contents = self._installed_instance.getcontents().keys() |
219 |
- old_libs = set([os.path.basename(x) for x in old_contents]).intersection(libmap) |
220 |
+ old_libs = set(old_contents).intersection(liblist) |
221 |
|
222 |
# get list of libraries from new package instance |
223 |
- mylibs = set([os.path.basename(x) for x in mycontents]).intersection(libmap) |
224 |
+ mylibs = set(mycontents).intersection(liblist) |
225 |
|
226 |
# check which libs are present in the old, but not the new package instance |
227 |
- preserve_libs = old_libs.difference(mylibs) |
228 |
+ candidates = old_libs.difference(mylibs) |
229 |
+ for x in old_contents: |
230 |
+ if os.path.islink(x) and os.path.realpath(x) in candidates: |
231 |
+ candidates.add(x) |
232 |
|
233 |
# ignore any libs that are only internally used by the package |
234 |
def has_external_consumers(lib, contents, otherlibs): |
235 |
- consumers = set(libmap[lib]) |
236 |
- contents_without_libs = [x for x in contents if not os.path.basename(x) in otherlibs] |
237 |
+ consumers = linkmap.findConsumers(lib) |
238 |
+ contents_without_libs = [x for x in contents if x not in otherlibs] |
239 |
|
240 |
# just used by objects that will be autocleaned |
241 |
if len(consumers.difference(contents_without_libs)) == 0: |
242 |
@@ -1696,23 +1716,34 @@ |
243 |
else: |
244 |
return True |
245 |
|
246 |
- for lib in list(preserve_libs): |
247 |
- if not has_external_consumers(lib, old_contents, preserve_libs): |
248 |
- preserve_libs.remove(lib) |
249 |
- # only preserve the lib if there is no other copy in the search path |
250 |
- for path in getlibpaths(): |
251 |
- fullname = os.path.join(path, lib) |
252 |
- if fullname not in old_contents and os.path.exists(fullname) and lib in preserve_libs: |
253 |
- preserve_libs.remove(lib) |
254 |
- |
255 |
- # get the real paths for the libs |
256 |
- preserve_paths = [x for x in old_contents if os.path.basename(x) in preserve_libs] |
257 |
- del old_contents, old_libs, mylibs, preserve_libs |
258 |
- |
259 |
+ for lib in list(candidates): |
260 |
+ if not has_external_consumers(lib, old_contents, candidates): |
261 |
+ candidates.remove(lib) |
262 |
+ # only preserve the lib if there is no other copy to use for each consumer |
263 |
+ keep = False |
264 |
+ for c in linkmap.findConsumers(lib): |
265 |
+ localkeep = True |
266 |
+ providers = linkmap.findProviders(c) |
267 |
+ for soname in providers: |
268 |
+ if lib in providers[soname]: |
269 |
+ for p in providers[soname]: |
270 |
+ if p not in candidates: |
271 |
+ localkeep = False |
272 |
+ break |
273 |
+ break |
274 |
+ if localkeep: |
275 |
+ keep = True |
276 |
+ |
277 |
+ del mylibs, mycontents, old_contents, liblist |
278 |
+ |
279 |
# inject files that should be preserved into our image dir |
280 |
import shutil |
281 |
missing_paths = [] |
282 |
- for x in preserve_paths: |
283 |
+ for x in candidates: |
284 |
+ # skip existing files so the 'new' libs aren't overwritten |
285 |
+ if os.path.exists(os.path.join(srcroot, x.lstrip(os.sep))): |
286 |
+ missing_paths.append(x) |
287 |
+ continue |
288 |
print "injecting %s into %s" % (x, srcroot) |
289 |
if not os.path.exists(os.path.join(destroot, x.lstrip(os.sep))): |
290 |
print "%s does not exist so can't be preserved" % x |
291 |
@@ -1730,14 +1761,14 @@ |
292 |
os.symlink(linktarget, os.path.join(srcroot, x.lstrip(os.sep))) |
293 |
if linktarget[0] != os.sep: |
294 |
linktarget = os.path.join(os.path.dirname(x), linktarget) |
295 |
- preserve_paths.append(linktarget) |
296 |
+ candidates.add(linktarget) |
297 |
else: |
298 |
shutil.copy2(os.path.join(destroot, x.lstrip(os.sep)), |
299 |
os.path.join(srcroot, x.lstrip(os.sep))) |
300 |
|
301 |
- preserve_paths = [x for x in preserve_paths if x not in missing_paths] |
302 |
+ preserve_paths = [x for x in candidates if x not in missing_paths] |
303 |
|
304 |
- del missing_paths |
305 |
+ del missing_paths, candidates |
306 |
|
307 |
# keep track of the libs we preserved |
308 |
self.vartree.dbapi.plib_registry.register(self.mycpv, self.settings["SLOT"], counter, preserve_paths) |
309 |
@@ -2275,7 +2306,7 @@ |
310 |
del conf_mem_file |
311 |
|
312 |
# regenerate reverse NEEDED map |
313 |
- self.vartree.dbapi.libmap.update() |
314 |
+ self.vartree.dbapi.linkmap.rebuild() |
315 |
|
316 |
#do postinst script |
317 |
self.settings["PORTAGE_UPDATE_ENV"] = \ |
318 |
|
319 |
Modified: main/trunk/pym/portage/sets/dbapi.py |
320 |
=================================================================== |
321 |
--- main/trunk/pym/portage/sets/dbapi.py 2008-05-02 05:09:48 UTC (rev 10080) |
322 |
+++ main/trunk/pym/portage/sets/dbapi.py 2008-05-02 08:20:39 UTC (rev 10081) |
323 |
@@ -2,14 +2,10 @@ |
324 |
# Distributed under the terms of the GNU General Public License v2 |
325 |
# $Id$ |
326 |
|
327 |
-from portage.versions import catsplit, catpkgsplit |
328 |
+from portage.versions import catsplit |
329 |
from portage.sets.base import PackageSet |
330 |
from portage.sets import SetConfigError, get_boolean |
331 |
-from portage.dbapi.vartree import dblink |
332 |
-from portage.util import grabfile |
333 |
|
334 |
-import os |
335 |
- |
336 |
__all__ = ["CategorySet", "EverythingSet"] |
337 |
|
338 |
class EverythingSet(PackageSet): |
339 |
@@ -108,71 +104,3 @@ |
340 |
return rValue |
341 |
multiBuilder = classmethod(multiBuilder) |
342 |
|
343 |
-class LibraryConsumerSet(PackageSet): |
344 |
- _operations = ["merge", "unmerge"] |
345 |
- |
346 |
- def __init__(self, vardbapi, debug=False): |
347 |
- super(LibraryConsumerSet, self).__init__() |
348 |
- self.dbapi = vardbapi |
349 |
- self.debug = debug |
350 |
- |
351 |
- def mapPathsToAtoms(self, paths): |
352 |
- rValue = set() |
353 |
- for cpv in self.dbapi.cpv_all(): |
354 |
- mysplit = catsplit(cpv) |
355 |
- link = dblink(mysplit[0], mysplit[1], myroot=self.dbapi.root, \ |
356 |
- mysettings=self.dbapi.settings, treetype='vartree', \ |
357 |
- vartree=self.dbapi.vartree) |
358 |
- if paths.intersection(link.getcontents()): |
359 |
- cat, pn = catpkgsplit(cpv)[:2] |
360 |
- slot = self.dbapi.aux_get(cpv, ["SLOT"])[0] |
361 |
- rValue.add("%s/%s:%s" % (cat, pn, slot)) |
362 |
- return rValue |
363 |
- |
364 |
- |
365 |
-class PreservedLibraryConsumerSet(LibraryConsumerSet): |
366 |
- def load(self): |
367 |
- reg = self.dbapi.plib_registry |
368 |
- libmap = self.dbapi.libmap.get() |
369 |
- consumers = set() |
370 |
- if reg: |
371 |
- for libs in reg.getPreservedLibs().values(): |
372 |
- for lib in libs: |
373 |
- paths = libmap.get(os.path.basename(lib), []) |
374 |
- consumers.update(paths) |
375 |
- else: |
376 |
- return |
377 |
- if not consumers: |
378 |
- return |
379 |
- self._setAtoms(self.mapPathsToAtoms(consumers)) |
380 |
- |
381 |
- def singleBuilder(cls, options, settings, trees): |
382 |
- return PreservedLibraryConsumerSet(trees["vartree"].dbapi) |
383 |
- singleBuilder = classmethod(singleBuilder) |
384 |
- |
385 |
-class MissingLibraryConsumerSet(LibraryConsumerSet): |
386 |
- _operations = ["merge", "unmerge"] |
387 |
- |
388 |
- def load(self): |
389 |
- atoms = set() |
390 |
- consumers = set() |
391 |
- for lib in self.dbapi.libmap.get(): |
392 |
- found=False |
393 |
- for searchdir in grabfile(os.path.join(os.sep, self.dbapi.root, "etc/ld.so.conf")): |
394 |
- if os.path.exists(os.path.join(searchdir, lib)): |
395 |
- found=True |
396 |
- break |
397 |
- if not found: |
398 |
- print "missing library: %s" % lib |
399 |
- print "consumers:" |
400 |
- for x in self.dbapi.libmap.get()[lib]: |
401 |
- print " ", x |
402 |
- consumers.update(self.dbapi.libmap.get()[lib]) |
403 |
- if not consumers: |
404 |
- return |
405 |
- self._setAtoms(self.mapPathsToAtoms(consumers)) |
406 |
- |
407 |
- def singleBuilder(cls, options, settings, trees): |
408 |
- debug = get_boolean(options, "debug", False) |
409 |
- return MissingLibraryConsumerSet(trees["vartree"].dbapi, debug=debug) |
410 |
- singleBuilder = classmethod(singleBuilder) |
411 |
|
412 |
Added: main/trunk/pym/portage/sets/libs.py |
413 |
=================================================================== |
414 |
--- main/trunk/pym/portage/sets/libs.py (rev 0) |
415 |
+++ main/trunk/pym/portage/sets/libs.py 2008-05-02 08:20:39 UTC (rev 10081) |
416 |
@@ -0,0 +1,50 @@ |
417 |
+# Copyright 2007 Gentoo Foundation |
418 |
+# Distributed under the terms of the GNU General Public License v2 |
419 |
+# $Id$ |
420 |
+ |
421 |
+from portage.sets.base import PackageSet |
422 |
+from portage.dbapi.vartree import dblink |
423 |
+from portage.versions import catsplit, catpkgsplit |
424 |
+ |
425 |
+import os |
426 |
+ |
427 |
+class LibraryConsumerSet(PackageSet): |
428 |
+ _operations = ["merge", "unmerge"] |
429 |
+ |
430 |
+ def __init__(self, vardbapi, debug=False): |
431 |
+ super(LibraryConsumerSet, self).__init__() |
432 |
+ self.dbapi = vardbapi |
433 |
+ self.debug = debug |
434 |
+ |
435 |
+ def mapPathsToAtoms(self, paths): |
436 |
+ rValue = set() |
437 |
+ for cpv in self.dbapi.cpv_all(): |
438 |
+ mysplit = catsplit(cpv) |
439 |
+ link = dblink(mysplit[0], mysplit[1], myroot=self.dbapi.root, \ |
440 |
+ mysettings=self.dbapi.settings, treetype='vartree', \ |
441 |
+ vartree=self.dbapi.vartree) |
442 |
+ if paths.intersection(link.getcontents()): |
443 |
+ cat, pn = catpkgsplit(cpv)[:2] |
444 |
+ slot = self.dbapi.aux_get(cpv, ["SLOT"])[0] |
445 |
+ rValue.add("%s/%s:%s" % (cat, pn, slot)) |
446 |
+ return rValue |
447 |
+ |
448 |
+ |
449 |
+class PreservedLibraryConsumerSet(LibraryConsumerSet): |
450 |
+ def load(self): |
451 |
+ reg = self.dbapi.plib_registry |
452 |
+ consumers = set() |
453 |
+ if reg: |
454 |
+ for libs in reg.getPreservedLibs().values(): |
455 |
+ for lib in libs: |
456 |
+ #print lib, self.dbapi.linkmap.findConsumers(lib) |
457 |
+ consumers.update(self.dbapi.linkmap.findConsumers(lib)) |
458 |
+ else: |
459 |
+ return |
460 |
+ if not consumers: |
461 |
+ return |
462 |
+ self._setAtoms(self.mapPathsToAtoms(consumers)) |
463 |
+ |
464 |
+ def singleBuilder(cls, options, settings, trees): |
465 |
+ return PreservedLibraryConsumerSet(trees["vartree"].dbapi) |
466 |
+ singleBuilder = classmethod(singleBuilder) |
467 |
|
468 |
|
469 |
Property changes on: main/trunk/pym/portage/sets/libs.py |
470 |
___________________________________________________________________ |
471 |
Name: svn:keywords |
472 |
+ Id |
473 |
|
474 |
-- |
475 |
gentoo-commits@l.g.o mailing list |