public inbox for gentoo-soc@lists.gentoo.org
 help / color / mirror / Atom feed
* [gentoo-soc] Progress Report - Revdep-rebuild
@ 2008-06-15  3:34 Lucian Poston
  0 siblings, 0 replies; 13+ messages in thread
From: Lucian Poston @ 2008-06-15  3:34 UTC (permalink / raw
  To: gentoo-soc

It's been a slow start due to the time it has taken me to understand
the dynamic linker and the various python modules in portage.
Initially, I intended to understand the current revdep-rebuild
implementation, but trying to follow that monstrosity of a bash script
only caused my eyes to glaze over. :)  So instead, I decided to go
another route and write an implementation of my own and deal with any
problems as they arise, rather than initially basing my implementation
off of the bash script.

I put the new RevdepSet module in a separate file. Should I instead
include this in one of the existing files? I couldn't find a clear
description of the purpose of the set classes within each file, so I
simply placed it in a new one. Also, did anyone have a more succinct,
descriptive name suggestion for the set already in mind?

Currently the class is nothing more than a skeleton of a package set
class, which I've been using to test the various dbapi interfaces.  It
does not build a set of useful atoms yet.

My plan over the next few days is to build a list of "needed"
libraries (either through dbapi or my own implementation similar to
linkmap), a list of installed libraries (I'm still unsure of the best
way to build this list.  Simply searching through lib directories
perhaps?), and comparing the two lists to find which are missing.  The
packages are already associated with the libraries in var/db/pkg, so
that shouldn't be a problem.  I'll see how that works and go from
there.

Are there any future plans to integrate the concept of recompiling
necessary binaries against newly updated libraries when upgrading
through emerge? Or is it more likely to stay as is with preserved-lib
functionality? I was just wondering about the futility of this whole
project in the future. :)

Should emerge revdep-rebuild rebuild the packages that are compiled
against preserved libraries? I assume no, since that functionality is
already present with preserved-libs, but I wanted to be sure.


Thanks,
Lucian
-- 
gentoo-soc@lists.gentoo.org mailing list



^ permalink raw reply	[flat|nested] 13+ messages in thread

* [gentoo-soc] Progress Report - Revdep-rebuild
@ 2008-06-15  3:55 Lucian Poston
  2008-06-17 15:55 ` Marius Mauch
  0 siblings, 1 reply; 13+ messages in thread
From: Lucian Poston @ 2008-06-15  3:55 UTC (permalink / raw
  To: gentoo-soc

It's been a slow start due to the time it has taken me to understand
the dynamic linker and the various python modules in portage.
Initially, I intended to understand the current revdep-rebuild
implementation, but trying to follow that monstrosity of a bash script
only caused my eyes to glaze over. :)  So instead, I decided to go
another route and write an implementation of my own and deal with any
problems as they arise, rather than initially basing my implementation
off of the bash script.

I put the new RevdepSet module in a separate file. Should I instead
include this in one of the existing files? I couldn't find a clear
description of the purpose of the set classes within each file, so I
simply placed it in a new one. Also, did anyone have a more succinct,
descriptive name suggestion for the set already in mind?

Currently the class is nothing more than a skeleton of a package set
class, which I've been using to test the various dbapi interfaces.  It
does not build a set of useful atoms yet.

My plan over the next few days is to build a list of "needed"
libraries (either through dbapi or my own implementation similar to
linkmap), a list of installed libraries (I'm still unsure of the best
way to build this list.  Simply searching through lib directories
perhaps?), and comparing the two lists to find which are missing.  The
packages are already associated with the libraries in var/db/pkg, so
that shouldn't be a problem.  I'll see how that works and go from
there.

Are there any future plans to integrate the concept of recompiling
necessary binaries against newly updated libraries when upgrading
through emerge? Or is it more likely to stay as is with preserved-lib
functionality? I was just wondering about the futility of this whole
project in the future. :)

Should emerge revdep-rebuild rebuild the packages that are compiled
against preserved libraries? I assume no, since that functionality is
already present with preserved-libs, but I wanted to be sure.


Thanks,
Lucian
-- 
gentoo-soc@lists.gentoo.org mailing list



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [gentoo-soc] Progress Report - Revdep-rebuild
  2008-06-15  3:55 Lucian Poston
@ 2008-06-17 15:55 ` Marius Mauch
  0 siblings, 0 replies; 13+ messages in thread
From: Marius Mauch @ 2008-06-17 15:55 UTC (permalink / raw
  To: gentoo-soc

Looks like my first mail disappeared somehow, lets try again:

On Sat, 14 Jun 2008 22:55:23 -0500
"Lucian Poston" <lucianposton@gmail.com> wrote:

> I put the new RevdepSet module in a separate file. Should I instead
> include this in one of the existing files? I couldn't find a clear
> description of the purpose of the set classes within each file, so I
> simply placed it in a new one. Also, did anyone have a more succinct,
> descriptive name suggestion for the set already in mind?

For the purpose of SoC it's better to keep it in a separate file,
though for deployment it's probably going to be integrated into
sets/libs.py. As for name, the old prototype was named
MissingLibraryConsumerSet (mainly due to inheriting from
LibraryConsumerSet, which you might also be interested in).

> My plan over the next few days is to build a list of "needed"
> libraries (either through dbapi or my own implementation similar to
> linkmap), a list of installed libraries (I'm still unsure of the best
> way to build this list.  Simply searching through lib directories
> perhaps?), and comparing the two lists to find which are missing.  The
> packages are already associated with the libraries in var/db/pkg, so
> that shouldn't be a problem.  I'll see how that works and go from
> there.

Sounds like a job for linkmap.findProviders(), but might be an idea to
implement multiple solutions for comparison/testing purposes
(see sets/security.py for one way to do that).

> Are there any future plans to integrate the concept of recompiling
> necessary binaries against newly updated libraries when upgrading
> through emerge? Or is it more likely to stay as is with preserved-lib
> functionality? I was just wondering about the futility of this whole
> project in the future. :)

Well, the only thing I'm somewhat planning atm is to (optionally)
automatically rebuild certain sets after an emerge session, but that's
just a plan for now and definitely won't happen before SoC is over.
Other than that we'll have to see how preserve-libs works out in
practice, but I don't expect it to go away that soon.
Btw, if you're worried that preserve-libs makes your project redundant,
it won't. It is supposed to reduce the need for it, but as it's
optional and only limited in scope there will always be a need for
revdep-rebuild functionality.

> Should emerge revdep-rebuild rebuild the packages that are compiled
> against preserved libraries? I assume no, since that functionality is
> already present with preserved-libs, but I wanted to be sure.

Better to not include preserved libs. Would be trivial to create a
wrapper set to include both individual sets if necessary.

Marius

-- 
Public Key at http://www.genone.de/info/gpg-key.pub

In the beginning, there was nothing. And God said, 'Let there be
Light.' And there was still nothing, but you could see a bit better.
-- 
gentoo-soc@lists.gentoo.org mailing list



^ permalink raw reply	[flat|nested] 13+ messages in thread

* [gentoo-soc] Progress Report - Revdep-rebuild
@ 2008-06-26  1:30 Lucian Poston
  2008-06-26 17:47 ` Marius Mauch
  0 siblings, 1 reply; 13+ messages in thread
From: Lucian Poston @ 2008-06-26  1:30 UTC (permalink / raw
  To: gentoo-soc; +Cc: Marius Mauch

As mentioned last week, I created lists of "needed" libs and
"installed" libs. I was hoping that the difference would produce a set
of "missing" libs, but I encountered several problems with that, which
I discuss below.

The list of "needed" libraries is found by calling findProviders() on
all installed files listed in each atom's CONTENTS. It's inefficient,
but once I'm able to find a solution to the problems mentioned below,
I will try a different implementation to build a list of "needed"
libs, most likely by creating another method in LinkageMap (if that is
okay).

A couple libraries that I've questions about:


libodbc.so; libodbcinst.so; libgdbm.so.2; libdb-3.1.so; libcrypto.so.0.9.6:

dev-java/sun-jdk-1.6.0.05/NEEDED.ELF.2:
386;/opt/sun-jdk-1.6.0.05/jre/lib/i386/libJdbcOdbc.so;libJdbcOdbc.so;$ORIGIN;libodbcinst.so,libodbc.so,libjvm.so,libc.so.6
app-office/openoffice-bin-2.4.0/NEEDED.ELF.2:
386;/usr/lib/openoffice/program/python-core-2.3.4/lib/lib-dynload/dbm.so;;;libgdbm.so.2,libpthread.so.0,libc.so.6

These libraries are needed as shown above; however, they are not in
the filesystem and consequently have no scanelf entries in any NEEDED
file. How are they dynamically linked when the library isn't
installed? I'm not sure what's going on with these libraries at all.


ld-linux.so.2 -> ld-2.6.1.so:

This is installed by sys-libs/glibc-2.6.1; however, it has no entry in
any NEEDED file. Scanning the library myself produces
EM_386;/lib/ld-2.6.1.so;ld-linux.so.2;;. I'd assume that the dynamic
linker would also have a NEEDED file entry for consistency, but it
doesn't. Is there a reason why?


libGLcore.so.1; libnvidia-tls.so.1:

Similar to above, these libraries from
x11-drivers/nvidia-drivers-169.09-r1 are installed and dynamically
linked to other libraries, but they have no entry in a NEEDED file.
For libGLcore.so.1, scanelf produced
EM_386;/usr/lib/opengl/nvidia/lib/libGLcore.so.169.09;libGLcore.so.1;;


libmix.so:

dev-libs/libmix-2.05/NEEDED.ELF.2:
386;/usr/lib/libmix.so;;;libc.so.6

It is a part of dev-libs/libmix-2.05 and needed by /usr/bin/nc, which
is in net-analyzer/netcat-110-r8. This library is installed and has an
entry in a NEEDED file, but that scanelf entry has no soname.


A couple other libraries (eg libpython2.3.so.1.0, libview.so.2,
libatkmm-1.6.so.1, libcrypto.so.0.9.7, libglibmm-2.4.so.1), which are
part of app-office/openoffice-bin-2.4.0 or
app-emulation/vmware-workstation-5.5.5.56455, are located in
directories, which are not in portage.util.getlibpaths().


As a consequence of the previous situations, the dict returned by
findProviders() includes all the previously needed libraries as keys,
but their associated values are the empty set. I'm unsure if these
libraries should be ignored or if some other action should be taken.

My current problem is that I was expecting findProviders() to return
the empty set for binaries which are missing libraries, and thus those
binaries need to be re-emerged, but the above situations complicate
things because those libraries also map to the empty set. Hopefully, I
am able to reduce these irregularities into more general cases so that
what I've done so far has not been in vain. :)

For the next week, I'll attempt to resolve these issues, which will
likely lead me back to reading the old revdep-rebuild script.

The code is hosted at
http://repo.or.cz/w/revdep-rebuild-reimplementation.git?a=tree


-Lucian
-- 
gentoo-soc@lists.gentoo.org mailing list



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [gentoo-soc] Progress Report - Revdep-rebuild
  2008-06-26  1:30 Lucian Poston
@ 2008-06-26 17:47 ` Marius Mauch
  0 siblings, 0 replies; 13+ messages in thread
From: Marius Mauch @ 2008-06-26 17:47 UTC (permalink / raw
  To: gentoo-soc

On Wed, 25 Jun 2008 20:30:02 -0500
"Lucian Poston" <lucianposton@gmail.com> wrote:

> As mentioned last week, I created lists of "needed" libs and
> "installed" libs. I was hoping that the difference would produce a set
> of "missing" libs, but I encountered several problems with that, which
> I discuss below.

Yeah, it's not as easy as it might appear ;)

> 
> The list of "needed" libraries is found by calling findProviders() on
> all installed files listed in each atom's CONTENTS. It's inefficient,
> but once I'm able to find a solution to the problems mentioned below,
> I will try a different implementation to build a list of "needed"
> libs, most likely by creating another method in LinkageMap (if that is
> okay).

Sure, basically just return the relevant field from
linkmap._obj_properties. But makre sure you understand how library
resolution works by reading ld.so(8), things like DT_RUNPATH makes
things a bit tricky.

> libodbc.so; libodbcinst.so; libgdbm.so.2; libdb-3.1.so;
> libcrypto.so.0.9.6:
> 
> dev-java/sun-jdk-1.6.0.05/NEEDED.ELF.2:
> 386;/opt/sun-jdk-1.6.0.05/jre/lib/i386/libJdbcOdbc.so;libJdbcOdbc.so;$ORIGIN;libodbcinst.so,libodbc.so,libjvm.so,libc.so.6
> app-office/openoffice-bin-2.4.0/NEEDED.ELF.2:
> 386;/usr/lib/openoffice/program/python-core-2.3.4/lib/lib-dynload/dbm.so;;;libgdbm.so.2,libpthread.so.0,libc.so.6
> 
> These libraries are needed as shown above; however, they are not in
> the filesystem and consequently have no scanelf entries in any NEEDED
> file. How are they dynamically linked when the library isn't
> installed? I'm not sure what's going on with these libraries at all.

Typical problem with binary packages. That's why revdep-rebuild doesn't
scan some directories.

> ld-linux.so.2 -> ld-2.6.1.so:
> 
> This is installed by sys-libs/glibc-2.6.1; however, it has no entry in
> any NEEDED file. Scanning the library myself produces
> EM_386;/lib/ld-2.6.1.so;ld-linux.so.2;;. I'd assume that the dynamic
> linker would also have a NEEDED file entry for consistency, but it
> doesn't. Is there a reason why?

Well, I've got the following in
sys-libs/glibc-2.8_p20080602/NEEDED.ELF.2:

X86_64;/lib64/ld-2.8.so;ld-linux-x86-64.so.2;;

So not sure why you don't have it. Try rebuilding glibc, maybe it was a
bug in some older version.

> libGLcore.so.1; libnvidia-tls.so.1:
> 
> Similar to above, these libraries from
> x11-drivers/nvidia-drivers-169.09-r1 are installed and dynamically
> linked to other libraries, but they have no entry in a NEEDED file.
> For libGLcore.so.1, scanelf produced
> EM_386;/usr/lib/opengl/nvidia/lib/libGLcore.so.169.09;libGLcore.so.1;;

Same as above, I've got the relevant entries here.

> libmix.so:
> 
> dev-libs/libmix-2.05/NEEDED.ELF.2:
> 386;/usr/lib/libmix.so;;;libc.so.6
> 
> It is a part of dev-libs/libmix-2.05 and needed by /usr/bin/nc, which
> is in net-analyzer/netcat-110-r8. This library is installed and has an
> entry in a NEEDED file, but that scanelf entry has no soname.

Interesting, might be a bug in that package.

> A couple other libraries (eg libpython2.3.so.1.0, libview.so.2,
> libatkmm-1.6.so.1, libcrypto.so.0.9.7, libglibmm-2.4.so.1), which are
> part of app-office/openoffice-bin-2.4.0 or
> app-emulation/vmware-workstation-5.5.5.56455, are located in
> directories, which are not in portage.util.getlibpaths().

See DT_RUNPATH in ld.so(8), and maybe look at the LinakgeMap code.

> As a consequence of the previous situations, the dict returned by
> findProviders() includes all the previously needed libraries as keys,
> but their associated values are the empty set. I'm unsure if these
> libraries should be ignored or if some other action should be taken.

To clarify: the keys of the returned dict are the soname of requried
libraries, and the values are lists of all providers in the library
path for the object. For the libraries listed above the primary thing
to do is to figure out why the entries are missing on your system. In
general when encountering an unexpected situation you should raise an
appropriate notice so it can be analyzed.

Marius

-- 
Public Key at http://www.genone.de/info/gpg-key.pub

In the beginning, there was nothing. And God said, 'Let there be
Light.' And there was still nothing, but you could see a bit better.
-- 
gentoo-soc@lists.gentoo.org mailing list



^ permalink raw reply	[flat|nested] 13+ messages in thread

* [gentoo-soc] Progress Report - Revdep-rebuild
@ 2008-07-12  3:13 Lucian Poston
  0 siblings, 0 replies; 13+ messages in thread
From: Lucian Poston @ 2008-07-12  3:13 UTC (permalink / raw
  To: gentoo-soc; +Cc: Marius Mauch

The print statements have been enclosed in debug conditionals, and a
good amount of comments have been added to the code, so hopefully it
is more readable.

I've changed the way that broken binaries are found.  Previously, the
difference between the sets of "needed" and "installed" libraries were
calculated to produce a set of "missing" libraries from which
consumers (binaries) were found.  Now, when findProviders() returns an
unsatisfied soname, the binary and soname are stored into the
brokenDependencies dict (there is a description in the code comments).
 This way the binaries are immediately available to calculate a set of
atoms via mapPathsToAtoms().  Also, now the providers of all binaries
are considered. Previously, if two binaries required the same soname,
only the lookup paths of the last binary would be considered, but now
all binaries are checked individually.  Organizing the dependencies in
this dict will likely be the same in the return value of the new
method in LinkageMap, so the addition shouldn't affect this class much
aside from moving some of the functionality into LinkageMap.

I was only able solve a few of the classes of problems I had with the
libraries that were listed in the last report.  Currently, binary
packages are what's left from those (and the weird libmix.so bug), so
binary packages aren't handled properly yet.  Emerging the
missingLibraryConsumerSet will result in superfluous packages due to
those packages.  Resolving that problem is my main focus for now, but
once binary packages are handled properly, I'll work on adding the
method in LinkageMap mentioned before.  I expect it to bring a decent
speed improvement since there will be no need to iterate over a large
number of non-binary files with access to _obj_properties in
LinkageMap.  After that, some user options will be added (if I can
come up with some useful functionalities I'd like to see added).

As far as user options, what should I plan to implement aside from
--library=NAME a la revdep-rebuild?

Lucian
-- 
gentoo-soc@lists.gentoo.org mailing list



^ permalink raw reply	[flat|nested] 13+ messages in thread

* [gentoo-soc] Progress Report - Revdep-rebuild
@ 2008-07-20  9:23 Lucian Poston
  0 siblings, 0 replies; 13+ messages in thread
From: Lucian Poston @ 2008-07-20  9:23 UTC (permalink / raw
  To: gentoo-soc; +Cc: Marius Mauch

Two major additions happened this week:

-A large part of the functionality that finds broken dependencies has
been moved into LinkageMap within pym/portage/dbapi/vartree.py.
listBrokenDependencies() and listProviders() were added as well as a
minor change to what was being added into the needed entries in
_obj_properties.  Empty strings were inserted into needed entries when
binaries had no needed entries in NEEDED.ELF.2.

-I resorted to masking directories and libraries to filter false
positives due to various reasons that are mentioned below.  The
existing SEARCH_DIRS_MASK and LD_LIBRARY_MASK variables from
/etc/revdep-rebuild/* are being used for now to mask special binaries
and directories, which are most often the result of binary packages.
This is the only way I've been able to filter binaries that are in
fact missing shared libraries according to my understanding of how the
dynamic linkers locates libraries (and what ldd reports).

vartree.py and sets.conf have been added to the repository at
http://repo.or.cz/w/revdep-rebuild-reimplementation.git

Replacing vartree.py and adding revdep.py and /etc/portage/sets.conf
should be enough to test out the set via emerge @missing-rebuild (you
may want to disable debug in sets.conf).  It's quite likely that many
packages are causing false positives.

Below are some examples of the types of edge cases I've encountered
and how they were resolved.

Lucian



------------------
www-client/mozilla-firefox-2.0.0.15
(/usr/lib/mozilla-firefox/plugins/libunixprintplugin.so)
386;/usr/lib/mozilla-firefox/plugins/libunixprintplugin.so;libunixprintplugin.so;$ORIGIN:/usr/lib/nspr;libxpcom.so,libxpcom_core.so,libplds4.so.6,libplc4.so.6,libnspr4.so.6,libpthread.so.0,libdl.so.2,libXt.so.6,libX11.so.6,libstdc++.so.6,libm.so.6,libgcc_s.so.1,libc.so.6

The previous requires the two following libs:

www-client/mozilla-firefox-2.0.0.15 (/usr/lib/mozilla-firefox/libxpcom.so)
386;/usr/lib/mozilla-firefox/libxpcom.so;libxpcom.so;$ORIGIN:/usr/lib/nspr;libplds4.so.6,libplc4.so.6,libnspr4.so.6,libpthread.so.0,libdl.so.2,libstdc++.so.6,libm.so.6,libgcc_s.so.1,libc.so.6

www-client/mozilla-firefox-2.0.0.15 (/usr/lib/mozilla-firefox/libxpcom_core.so)
386;/usr/lib/mozilla-firefox/libxpcom_core.so;libxpcom_core.so;$ORIGIN:/usr/lib/nspr;libplds4.so.6,libplc4.so.6,libnspr4.so.6,libpthread.so.0,libdl.so.2,libgtk-x11-2.0.so.0,libgdk-x11-2.0.so.0,libatk-1.0.so.0,libgdk_pixbuf-2.0.so.0,libpangocairo-1.0.so.0,libpango-1.0.so.0,libcairo.so.2,libgobject-2.0.so.0,libgmodule-2.0.so.0,libglib-2.0.so.0,libstdc++.so.6,libm.so.6,libgcc_s.so.1,libc.so.6

Notice that the two libs are needed; however, they are not located in
DT_RUNPATH of libunixprintplugin.so.
The rpath of libunixprintplugin.so includes
/usr/lib/mozilla-firefox/plugins; however, the required libs are in
/usr/lib/mozilla-firefox.

It would appear that the runpath for
libunixprintplugin.so should include $ORIGIN/.. rather than $ORIGIN.
I added a mask for /usr/lib/mozilla-firefox/plugins to deal with this.

------------------
app-emulation/vmware-workstation-5.5.7.91707
(/opt/vmware/workstation/lib/bin/vmware)
386;/opt/vmware/workstation/lib/bin/vmware;;;libdl.so.2,libm.so.6,libX11.so.6,libXext.so.6,libXi.so.6,libexpat.so.0,libfontconfig.so.1,libfreetype.so.6,libXrender.so.1,libXft.so.2,libglib-2.0.so.0,libgmodule-2.0.so.0,libgobject-2.0.so.0,libgthread-2.0.so.0,libatk-1.0.so.0,libpango-1.0.so.0,libpangoft2-1.0.so.0,libpangoxft-1.0.so.0,libpangox-1.0.so.0,libgdk-x11-2.0.so.0,libgdk_pixbuf-2.0.so.0,libgtk-x11-2.0.so.0,libgcc_s.so.1,libstdc++.so.5,libsigc-2.0.so.0,libglibmm-2.4.so.1,libglibmm_generate_extra_defs-2.4.so.1,libatkmm-1.6.so.1,libpangomm-1.4.so.1,libgdkmm-2.4.so.1,libgtkmm-2.4.so.1,libart_lgpl_2.so.2,libxml2.so.2,libglade-2.0.so.0,libgnomecanvas-2.so.0,libgnomecanvasmm-2.6.so.1,librsvg-2.so.2,libview.so.2,libsexy.so.1,libsexymm.so.1,libpthread.so.0,libz.so.1,libc.so.6,ld-linux.so.2

The previous requires the following:

app-emulation/vmware-workstation-5.5.7.91707
(/opt/vmware/workstation/lib/lib/libview.so.2/libview.so.2)
386;/opt/vmware/workstation/lib/lib/libview.so.2/libview.so.2;libview.so.2;/usr/lib/.;libgtkmm-2.4.so.1,libgdkmm-2.4.so.1,libatkmm-1.6.so.1,libgtk-x11-2.0.so.0,libpangomm-1.4.so.1,libglibmm-2.4.so.1,libsigc-2.0.so.0,libgdk-x11-2.0.so.0,libatk-1.0.so.0,libgdk_pixbuf-2.0.so.0,libpangoxft-1.0.so.0,libpangox-1.0.so.0,libpango-1.0.so.0,libgobject-2.0.so.0,libgmodule-2.0.so.0,libdl.so.2,libglib-2.0.so.0,libstdc++.so.5,libm.so.6,libc.so.6,libgcc_s.so.1

app-emulation/vmware-workstation-5.5.7.91707
(/opt/vmware/workstation/lib/lib/libpangomm-1.4.so.1/libpangomm-1.4.so.1)
386;/opt/vmware/workstation/lib/lib/libpangomm-1.4.so.1/libpangomm-1.4.so.1;libpangomm-1.4.so.1;/usr/lib/.;libglibmm-2.4.so.1,libsigc-2.0.so.0,libpango-1.0.so.0,libgobject-2.0.so.0,libgmodule-2.0.so.0,libdl.so.2,libglib-2.0.so.0,libstdc++.so.5,libm.so.6,libc.so.6,libgcc_s.so.1

app-emulation/vmware-workstation-5.5.7.91707
(/opt/vmware/workstation/lib/lib/libexpat.so.0/libexpat.so.0)
386;/opt/vmware/workstation/lib/lib/libexpat.so.0/libexpat.so.0;libexpat.so.0;;libc.so.6

Not in rpath.  I assume this is a bug or special binaries.
/opt/vmware/workstation/lib/bin isn't in $PATH anyhow.  I don't know
why binary packages have to be so rebellious.  I had to add a mask for
/opt/vmware/workstation/lib.

--------------------
dev-java/sun-jdk-1.6.0.06 (/opt/sun-jdk-1.6.0.06/jre/lib/i386/libjawt.so)
386;/opt/sun-jdk-1.6.0.06/jre/lib/i386/libjawt.so;libjawt.so;$ORIGIN;libmawt.so,libjvm.so,libc.so.6

The previous requires one of the following:

dev-java/sun-jdk-1.6.0.06
(/opt/sun-jdk-1.6.0.06/jre/lib/i386/motif21/libmawt.so)
386;/opt/sun-jdk-1.6.0.06/jre/lib/i386/xawt/libmawt.so;libmawt.so;$ORIGIN:$ORIGIN/..;libpthread.so.0,libm.so.6,libXext.so.6,libX11.so.6,libdl.so.2,libXtst.so.6,libXi.so.6,libjvm.so,libc.so.6

dev-java/sun-jdk-1.6.0.06
(/opt/sun-jdk-1.6.0.06/jre/lib/i386/headless/libmawt.so)
386;/opt/sun-jdk-1.6.0.06/jre/lib/i386/motif21/libmawt.so;libmawt.so;$ORIGIN:$ORIGIN/..;libXp.so.6,libXtst.so.6,libXext.so.6,libX11.so.6,libXi.so.6,libjvm.so,libm.so.6,libdl.so.2,libc.so.6

dev-java/sun-jdk-1.6.0.06 (/opt/sun-jdk-1.6.0.06/jre/lib/i386/xawt/libmawt.so)
386;/opt/sun-jdk-1.6.0.06/jre/lib/i386/headless/libmawt.so;libmawt.so;$ORIGIN:$ORIGIN/..;libjvm.so,libm.so.6,libdl.so.2,libc.so.6

Not in rpath. The rpath of libjawt.so includes
/opt/sun-jdk-1.6.0.06/jre/lib/i386, but the required lib(s) are a
level deeper.  The existing masks in /etc/revdep-rebuild/* covered these.

--------------------
dev-java/sun-jdk-1.6.0.06 (/opt/sun-jdk-1.6.0.06/jre/lib/i386/libJdbcOdbc.so)
386;/opt/sun-jdk-1.6.0.06/jre/lib/i386/libJdbcOdbc.so;libJdbcOdbc.so;$ORIGIN;libodbcinst.so,libodbc.so,libjvm.so,libc.so.6

The previous requires libodbcinst.so and libodbc.so, which are not
installed by this package or any dependencies.
The existing masks in /etc/revdep-rebuild/* covered these.

--------------------
app-office/openoffice-bin-2.4.1
(/usr/lib/openoffice/program/hatchwindowfactory.uno.so ->
hatchwindowfactory.uno.so.1.1)
386;/usr/lib/openoffice/program/hatchwindowfactory.uno.so.1.1;;$ORIGIN:$ORIGIN/../ure-link/lib;libtk680li.so,libvcl680li.so,libtl680li.so,libuno_cppuhelpergcc3.so.3,libuno_cppu.so.3,libuno_sal.so.3,libdl.so.2,libpthread.so.0,libstlport_gcc.so,libstdc++.so.6,libm.so.6,libgcc_s.so.1,libc.so.6

The previous requires the following:

app-office/openoffice-bin-2.4.1
(/usr/lib/openoffice/program/libstlport_gcc.so -> libstlport_gcc.so.1)
386;/usr/lib/openoffice/program/libstlport_gcc.so.1;;$ORIGIN;libm.so.6,libc.so.6

app-office/openoffice-bin-2.4.1
(/usr/lib/openoffice/program/libvcl680li.so -> libvcl680li.so.1.1)
386;/usr/lib/openoffice/program/libvcl680li.so.1.1;;$ORIGIN:$ORIGIN/../ure-link/lib;libpsp680li.so,libsot680li.so,libutl680li.so,libtl680li.so,libi18nisolang1gcc3.so,libcomphelp4gcc3.so,libucbhelper4gcc3.so,libuno_cppuhelpergcc3.so.3,libuno_cppu.so.3,libvos3gcc3.so,libuno_sal.so.3,libbasegfx680li.so,libicuuc.so.36,libicule.so.36,libjvmaccessgcc3.so.3,libfreetype.so.6,libX11.so.6,libXext.so.6,libdl.so.2,libpthread.so.0,libstlport_gcc.so,libstdc++.so.6,libm.so.6,libgcc_s.so.1,libc.so.6

app-office/openoffice-bin-2.4.1
(/usr/lib/openoffice/program/libtk680li.so -> libtk680li.so.1.1)
386;/usr/lib/openoffice/program/libtk680li.so.1.1;;$ORIGIN:$ORIGIN/../ure-link/lib;libvos3gcc3.so,libvcl680li.so,libsot680li.so,libutl680li.so,libtl680li.so,libcomphelp4gcc3.so,libuno_cppuhelpergcc3.so.3,libuno_cppu.so.3,libuno_sal.so.3,libX11.so.6,libdl.so.2,libpthread.so.0,libstlport_gcc.so,libstdc++.so.6,libm.so.6,libgcc_s.so.1,libc.so.6

The required libs are in rpath; however, they are missing sonames, so
LinkageMap can't add provider info into _libs.
Bugs in the package, I assume.  A little hack in
listBrokenDependencies() covered the case of missing sonames within
libraries (though these happen to be masked anyway).

--------------------
net-analyzer/netcat-110-r8 (/usr/bin/nc)
386;/usr/bin/nc;;;libmix.so,libc.so.6

The previous requires:

dev-libs/libmix-2.05 (/usr/lib/libmix.so)
386;/usr/lib/libmix.so;;;libc.so.6

Same as above. Missing soname in libmix.so.
Seems to be a bug.

------------------
x11-drivers/nvidia-drivers-173.14.09
(/usr/lib/opengl/nvidia/lib/libGL.so -> libGL.so.173.14.09)
386;/usr/lib/opengl/nvidia/lib/libGL.so.173.14.09;libGL.so.1;;libGLcore.so.1,libnvidia-tls.so.1,libm.so.6,libXext.so.6,libX11.so.6,libdl.so.2,libc.so.6

The previous requires the following:

x11-drivers/nvidia-drivers-173.14.09
(/usr/lib/opengl/nvidia/lib/libnvidia-tls.so ->
../tls/libnvidia-tls.so)
386;/usr/lib/opengl/nvidia/tls/libnvidia-tls.so.173.14.09;libnvidia-tls.so.1;;

The symlink is in rpath, but target is not. Only the target's path is
stored in LinkageMap, so the need is unsatisfied according to
findProviders().
listBrokenDependencies() checks for this case (only symlinks in runpath).

--------------------



^ permalink raw reply	[flat|nested] 13+ messages in thread

* [gentoo-soc] Progress Report - Revdep-rebuild
@ 2008-07-27  6:28 Lucian Poston
  0 siblings, 0 replies; 13+ messages in thread
From: Lucian Poston @ 2008-07-27  6:28 UTC (permalink / raw
  To: gentoo-soc; +Cc: Marius Mauch

An option was added to the missing-rebuild set to allow a user to
emerge the set of packages containing consumers of specified
libraries, similar to the revdep-rebuild --library flag.  Since flags
can't currently be passed to packages sets through the portage cli,
the user must set the environment variable LIBRARY to the library's
name or a regular expression to match libraries in order to invoke
this functionality.

A couple bugs related to broken or missing symlinks to libraries were
fixed.  There is one known bug remaining related to symlinks, which
was introduced as a result of other fixes.  Basically, if a target of
a symlink points to a library that does not provide the soname (for
example, /usr/lib/libfoo.so -> libnotfoo.so), binaries that require
/usr/lib/libfoo.so would be potentially broken and ignored.  The fix
that I have in mind is to check the target's soname entry in
LinkageMap._obj_properties; however, fixing it will result in
unnecessary packages in the package set (due to libraries missing an
SONAME in it's NEEDED.ELF.2 entry eg libmix.so).  Anyway, I figure it
would be better to have false positives than overlooking potential
broken dependencies.

The MissingLibraryConsumerSet.load method was split into 3 smaller
methods to which I've added docstrings conforming to the Portage
Docstring Spec, which only recently came to attention unfortunately.
The code is hopefully easier to digest now.

I never liked the idea of depending on the masks in
/etc/revdep-rebuild/* to remove false positives, so this week I'll try
a different approach in which binaries not found in PATH or library
directories will be ignored.  This should reduce the need for
directory and library masks (or more unlikely remove it entirely).
Although, I suspect that this will miss some broken dependencies, so
I'll have a good bit of testing to do.

Lucian



^ permalink raw reply	[flat|nested] 13+ messages in thread

* [gentoo-soc] Progress Report - Revdep-rebuild
@ 2008-08-01 21:24 Lucian Poston
  0 siblings, 0 replies; 13+ messages in thread
From: Lucian Poston @ 2008-08-01 21:24 UTC (permalink / raw
  To: gentoo-soc; +Cc: Marius Mauch

A new method, listBrokenLibtoolLibraries, was added to
MissingLibraryConsumerSet which basically checks that all dependencies
in libtool libraries exist on the filesystem.  It appears to work well.

LinkageMap.listBrokenDependencies has been rewritten as
LinkageMap.listBrokenBinaries in order to make the method more
readable and more efficient.  I implemented a cache within the method
as a class to avoid multiple calls to os.path.isfile and
os.path.realpath for the same file.

I had another idea for filtering packages without using masks.
Starting with the set of binaries in PATH variables and shared library
directories, the providers of those binaries will be added to the set,
and their providers will be added, etc.  The resulting set will be the
set of ancestors of the original binaries.  This is implemented in
LinkageMap.listProvidersForReachableBinaries (wonderful name, I know),
but so far it has not worked well in testing.  It misses binaries in
various paths.  For example, on my system it only
finds about 2400 binaries (compare that to over 5000 that were found
using the previous method).  This new method is disabled for now until
(if) I can get it to perform as well as LinkageMap.listBrokenBinaries.

Lastly, the files have been integrated into 2.2_rc6, revdep.py is
merged into libs.py, and patches have been added into the repository
(I should have done this long ago :/ ).  To test it out, you want to
apply the two patches to /usr/lib/portage/pym/portage/dbapi/vartree.py
and /usr/lib/portage/pym/portage/sets/libs.py in portage-2.2_rc6 and
add the contents of sets.conf in the repository to
/etc/portage/sets.conf.  The goods are at
http://repo.or.cz/w/revdep-rebuild-reimplementation.git?a=tree for
anyone interested.

Lucian



^ permalink raw reply	[flat|nested] 13+ messages in thread

* [gentoo-soc] Progress Report - Revdep-rebuild
@ 2008-08-11 23:12 Lucian Poston
  0 siblings, 0 replies; 13+ messages in thread
From: Lucian Poston @ 2008-08-11 23:12 UTC (permalink / raw
  To: gentoo-soc; +Cc: Marius Mauch

[-- Attachment #1: Type: text/plain, Size: 2656 bytes --]

This week didn't see much movement in the repository due to life
happening (moving and lack of internet at new apt).  On the plus side,
I believe the majority of the work is complete, and I urge everyone to
give it a test to help me discover problems.  :)

I'm happy with the overall results so far.  One thing that irks me is
the dependence on directory and library masks found in
/etc/revdep-rebuild/*.  I was really hoping to find a more elegant
solution than parsing masks from config files, but all my attempts at
a different solution were not successful.  Unfortunately, as long as
masks are used, there is the potential of false positives for
libraries that are not properly masked, which as far as I know is the
only major issue.

My work over the last week was largely polishing details in
docstrings/comments and contemplating final modifications (or future
endeavors).  During this last week of gsoc, I'm going to (hopefully)
improve the performance of LinkageMap and MissingLibraryConsumerSet
before the deadline.  It was suggested on the gentoo-portage-dev
mailing list [1] that efficiency in path comparisons in LinkageMap
could be improved by comparing inodes rather than os.path.realpath in
python's os.path module, which is what I started to implement
recently; however, this appears to require changes outside of the
class, which will take time to finalize e.g. during the unmerge
process, LinkageMap.rebuild is called after files have been removed
but before /var/db/pkg has been correspondingly updated, which is
problematic since.  I may not get all the kinks worked out of changing
LinkageMap before the gsoc deadline, so I'll have something to work on
afterwards.  :)

The attached patches add the library dependency rebuilder as a set,
@missing-rebuild, to portage-2.2_rc6.  Similar to the --library flag
in revdep-rebuild, the user can additionally emerge the set of
packages containing consumers of libraries matched by a (python)
regular expression; however, until a better solution is found, the
regexp must be passed through the LIBRARY environment variable to
enable that feature.

Attached are patches for
/usr/lib/portage/pym/portage/dbapi/vartree.py,
/usr/lib/portage/pym/portage/sets/libs.py and
/usr/share/portage/config/sets.conf, which can also be found in the
repository.[2]  After applying the three patches, one can test the set
with emerge -p @missing-rebuild.  If anyone is brave enough to do so,
let me know how it goes!

Lucian


[1] http://archives.gentoo.org/gentoo-portage-dev/msg_abcbac026ed8670f6dc61bb28be6151e.xml
[2] http://repo.or.cz/w/revdep-rebuild-reimplementation.git?a=tree;h=refs/heads/rc1;hb=rc1

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: libs.py.2.2_rc6.patch --]
[-- Type: text/x-patch; name=libs.py.2.2_rc6.patch, Size: 11563 bytes --]

--- libs.py.2.2_rc6	2008-08-01 15:41:14.000000000 -0500
+++ pym/portage/sets/libs.py	2008-08-11 13:38:01.000000000 -0500
@@ -2,10 +2,18 @@
 # Distributed under the terms of the GNU General Public License v2
 # $Id: libs.py 10759 2008-06-22 04:04:50Z zmedico $
 
+import os
+import re
+import time
+from portage.dbapi.vartree import dblink
+from portage.versions import catsplit
 from portage.sets.base import PackageSet
 from portage.sets import get_boolean
 from portage.versions import catpkgsplit
 
+__all__ = ["LibraryConsumerSet", "PreservedLibraryConsumerSet",
+		"MissingLibraryConsumerSet"]
+
 class LibraryConsumerSet(PackageSet):
 	_operations = ["merge", "unmerge"]
 
@@ -45,3 +53,311 @@
 		debug = get_boolean(options, "debug", False)
 		return PreservedLibraryConsumerSet(trees["vartree"].dbapi, debug)
 	singleBuilder = classmethod(singleBuilder)
+
+
+class MissingLibraryConsumerSet(LibraryConsumerSet):
+
+	"""
+	This class is the set of packages to emerge due to missing libraries.
+
+	This class scans binaries for missing and broken shared library dependencies
+	and fixes them by emerging the packages containing the broken binaries.
+
+	The user may also emerge packages containing consumers of specified
+	libraries by passing the name or a python regular expression through the
+	environment variable, LIBRARY.  Due to a limitation in passing flags to
+	package sets through the portage cli, the user must set environment
+	variables to modify the behaviour of this package set.  So if the
+	environment variable LIBRARY is set, the behaviour of this set changes.
+
+	"""
+
+	description = "The set of packages to emerge due to missing libraries."
+	_operations = ["merge"]
+
+	def __init__(self, vardbapi, debug=False):
+		super(MissingLibraryConsumerSet, self).__init__(vardbapi, debug)
+		# FIXME Since we can't get command line arguments from the user, the
+		# soname can be passed through an environment variable for now.
+		self.libraryRegexp = os.getenv("LIBRARY")
+		self.root = self.dbapi.root
+		self.linkmap = self.dbapi.linkmap
+
+	def load(self):
+		# brokenDependencies: object -> set-of-unsatisfied-dependencies, where
+		# object is an installed binary/library and
+		# set-of-unsatisfied-dependencies are sonames or libraries required by
+		# the object but have no corresponding libraries to fulfill the
+		# dependency.
+		brokenDependencies = {}
+		atoms = set()
+
+		# If the LIBRARY environment variable is set, the resulting package set
+		# will be packages containing consumers of the libraries matched by the
+		# variable.
+		if self.libraryRegexp:
+			atoms = self.findAtomsOfLibraryConsumers(self.libraryRegexp)
+			self._setAtoms(atoms)
+			if self.debug:
+				print
+				print "atoms to be emerged:"
+				for x in sorted(atoms):
+					print x
+			return
+
+		# Get the list of broken dependencies from LinkageMap.
+		if self.debug:
+			timeStart = time.time()
+		brokenDependencies = self.linkmap.listBrokenBinaries()
+		if self.debug:
+			timeListBrokenBinaries = time.time() - timeStart
+
+		# Add broken libtool libraries into the brokenDependencies dict.
+		if self.debug:
+			timeStart = time.time()
+		brokenDependencies.update(self.listBrokenLibtoolLibraries())
+		if self.debug:
+			timeLibtool = time.time() - timeStart
+
+		# FIXME Too many atoms may be emerged because libraries in binary
+		# packages are not being handled properly eg openoffice, nvidia-drivers,
+		# sun-jdk.  Certain binaries are run in an environment where additional
+		# library paths are added via LD_LIBRARY_PATH.  Since these paths aren't
+		# registered in _obj_properties, they appear broken (and are if not run
+		# in the correct environment).  I have to determine if libraries and lib
+		# paths should be masked using /etc/revdep-rebuild/* as done in
+		# revdep-rebuild or if there is a better way to identify and deal with
+		# these problematic packages (or if something entirely different should
+		# be done).  For now directory and library masks are used.
+
+		# Remove masked directories and libraries.
+		if self.debug:
+			timeStart = time.time()
+		if brokenDependencies:
+			brokenDependencies = self.removeMaskedDependencies(brokenDependencies)
+		if self.debug:
+			timeMask = time.time() - timeStart
+
+		# Determine atoms to emerge based on broken objects in
+		# brokenDependencies.
+		if self.debug:
+			timeStart = time.time()
+		if brokenDependencies:
+			atoms = self.mapPathsToAtoms(set(brokenDependencies.keys()))
+		if self.debug:
+			timeAtoms = time.time() - timeStart
+
+		# Debug output
+		if self.debug:
+			print
+			print len(brokenDependencies), "brokenDependencies:"
+			for x in sorted(brokenDependencies.keys()):
+				print
+				print x, "->"
+				print '\t', brokenDependencies[x]
+			print
+			print "atoms to be emerged:"
+			for x in sorted(atoms):
+				print x
+			print
+			print "Broken binaries time:", timeListBrokenBinaries
+			print "Broken libtool time:", timeLibtool
+			print "Remove mask time:", timeMask
+			print "mapPathsToAtoms time:", timeAtoms
+			print
+
+		self._setAtoms(atoms)
+
+	def removeMaskedDependencies(self, dependencies):
+		"""
+		Remove all masked dependencies and return the updated mapping.
+
+		@param dependencies: dependencies from which to removed masked
+			dependencies
+		@type dependencies: dict (example: {'/usr/bin/foo': set(['libfoo.so'])})
+		@rtype: dict
+		@return: shallow copy of dependencies with masked items removed
+
+		"""
+		rValue = dependencies.copy()
+		dirMask, libMask = self.getDependencyMasks()
+
+		# Remove entries that are masked.
+		if dirMask or libMask:
+			if self.debug:
+				print "The following are masked:"
+			for binary, libSet in rValue.items():
+				for dir in dirMask:
+					# Check if the broken binary lies within the masked directory or
+					# its subdirectories.
+					# XXX Perhaps we should allow regexps as masks.
+					if binary.startswith(dir):
+						del rValue[binary]
+						if self.debug:
+							print "dirMask:",binary
+						break
+				# Check if all the required libraries are masked.
+				if binary in rValue and libSet.issubset(libMask):
+					del rValue[binary]
+					if self.debug:
+						print "libMask:", binary, libSet & libMask
+
+		if self.debug:
+			print
+			print "Directory mask:", dirMask
+			print
+			print "Library mask:", libMask
+
+		return rValue
+
+	def getDependencyMasks(self):
+		"""
+		Return all dependency masks as a tuple.
+
+		@rtype: 2-tuple of sets of strings
+		@return: 2-tuple in which the first component is a set of directory
+			masks and the second component is a set of library masks
+
+		"""
+		dirMask = set()
+		libMask = set()
+		_dirMask_re = re.compile(r'SEARCH_DIRS_MASK\s*=\s*"([^"]*)"')
+		_libMask_re = re.compile(r'LD_LIBRARY_MASK\s*=\s*"([^"]*)"')
+		lines = []
+
+		# Reads the contents of /etc/revdep-rebuild/*
+		libMaskDir = os.path.join(self.root, "etc", "revdep-rebuild")
+		if os.path.exists(libMaskDir):
+			for file in os.listdir(libMaskDir):
+				try:
+					f = open(os.path.join(libMaskDir, file), "r")
+					try:
+						lines.extend(f.readlines())
+					finally:
+						f.close()
+				except IOError: # OSError?
+					continue
+			# The following parses SEARCH_DIRS_MASK and LD_LIBRARY_MASK variables
+			# from /etc/revdep-rebuild/*
+			for line in lines:
+				matchDir = _dirMask_re.match(line)
+				matchLib = _libMask_re.match(line)
+				if matchDir:
+					dirMask.update(set(matchDir.group(1).split()))
+				if matchLib:
+					libMask.update(set(matchLib.group(1).split()))
+
+		# These directories contain specially evaluated libraries.
+		# app-emulation/vmware-workstation-6.0.1.55017
+		dirMask.add('/opt/vmware/workstation/lib')
+		# app-emulation/vmware-server-console-1.0.6.91891
+		dirMask.add('/opt/vmware/server/console/lib')
+		# www-client/mozilla-firefox-2.0.0.15
+		dirMask.add('/usr/lib/mozilla-firefox/plugins')
+		dirMask.add('/usr/lib64/mozilla-firefox/plugins')
+		# app-office/openoffice-2.4.1
+		dirMask.add('/opt/OpenOffice')
+		dirMask.add('/usr/lib/openoffice')
+		# dev-libs/libmix-2.05  libmix.so is missing soname entry
+		libMask.add('libmix.so')
+
+		return (dirMask, libMask)
+
+	def findAtomsOfLibraryConsumers(self, searchString):
+		"""
+		Return atoms containing consumers of libraries matching the argument.
+
+		@param searchString: a string used to search for libraries
+		@type searchString: string to be compiled as a regular expression
+			(example: 'libfoo.*')
+		@rtype: set of strings
+		@return: the returned set of atoms are valid to be used by package sets
+
+		"""
+		atoms = set()
+		consumers = set()
+		matchedLibraries = set()
+		libraryObjects = []
+		_librarySearch_re = re.compile(searchString)
+
+		# Find libraries matching searchString.
+		libraryObjects = self.linkmap.listLibraryObjects()
+		for library in libraryObjects:
+			m = _librarySearch_re.search(library)
+			if m:
+				matchedLibraries.add(library)
+				consumers.update(self.linkmap.findConsumers(library))
+
+		if self.debug:
+			print
+			print "Consumers of the following libraries will be emerged:"
+			for x in matchedLibraries:
+				print x
+
+		if consumers:
+			# The following prevents emerging the packages that own the matched
+			# libraries.  Note that this will prevent updating the packages owning
+			# the libraries if there are newer versions available in the installed
+			# slot.  See bug #30095
+			atoms = self.mapPathsToAtoms(consumers)
+			libraryOwners = self.mapPathsToAtoms(matchedLibraries)
+			atoms.difference_update(libraryOwners)
+
+		return atoms
+
+	def listBrokenLibtoolLibraries(self):
+		"""
+		Find broken libtool libraries and their missing dependencies.
+
+		@rtype: dict (example: {'/lib/libfoo.la': set(['/lib/libbar.la'])})
+		@return: The return value is a library -> set-of-libraries mapping, where
+			library is a broken library and the set consists of dependencies
+			needed by library that do not exist on the filesystem.
+
+		"""
+		rValue = {}
+		lines = []
+		dependencies = []
+		_la_re = re.compile(r".*\.la$")
+		_dependency_libs_re = re.compile(r"^dependency_libs\s*=\s*'(.*)'")
+
+		# Loop over the contents of all packages.
+		for cpv in self.dbapi.cpv_all():
+			mysplit = catsplit(cpv)
+			link = dblink(mysplit[0], mysplit[1], myroot=self.dbapi.root, \
+					mysettings=self.dbapi.settings, treetype='vartree', \
+					vartree=self.dbapi.vartree)
+			for file in link.getcontents():
+				# Check if the file ends with '.la'.
+				matchLib = _la_re.match(file)
+				if matchLib:
+					# Read the lines from the library.
+					lines = []
+					try:
+						f = open(file, "r")
+						try:
+							lines.extend(f.readlines())
+						finally:
+							f.close()
+					except IOError:
+						continue
+					# Find the line listing the dependencies.
+					for line in lines:
+						matchLine = _dependency_libs_re.match(line)
+						if matchLine:
+							dependencies = matchLine.group(1).split()
+							# For each dependency that is a pathname (begins with
+							# os.sep), check that it exists on the filesystem.  If it
+							# does not exist, then add the library and the missing
+							# dependency to rValue.
+							for dependency in dependencies:
+								if dependency[0] == os.sep and \
+										not os.path.isfile(dependency):
+									rValue.setdefault(file, set()).add(dependency)
+
+		return rValue
+
+	def singleBuilder(self, options, settings, trees):
+		debug = get_boolean(options, "debug", False)
+		return MissingLibraryConsumerSet(trees["vartree"].dbapi, debug)
+	singleBuilder = classmethod(singleBuilder)

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: vartree.py.2.2_rc6.patch --]
[-- Type: text/x-patch; name=vartree.py.2.2_rc6.patch, Size: 7256 bytes --]

--- vartree.py.2.2_rc6	2008-08-01 15:41:03.000000000 -0500
+++ pym/portage/dbapi/vartree.py	2008-08-11 15:45:55.000000000 -0500
@@ -173,8 +173,10 @@
 			arch = fields[0]
 			obj = os.path.realpath(fields[1])
 			soname = fields[2]
-			path = fields[3].replace("${ORIGIN}", os.path.dirname(obj)).replace("$ORIGIN", os.path.dirname(obj)).split(":")
-			needed = fields[4].split(",")
+			path = filter(None, fields[3].replace(
+				"${ORIGIN}", os.path.dirname(obj)).replace(
+				"$ORIGIN", os.path.dirname(obj)).split(":"))
+			needed = filter(None, fields[4].split(","))
 			if soname:
 				libs.setdefault(soname, {arch: {"providers": [], "consumers": []}})
 				libs[soname].setdefault(arch, {"providers": [], "consumers": []})
@@ -188,6 +190,159 @@
 		self._libs = libs
 		self._obj_properties = obj_properties
 
+	def listBrokenBinaries(self):
+		"""
+		Find binaries and their needed sonames, which have no providers.
+
+		@rtype: dict (example: {'/usr/bin/foo': set(['libbar.so'])})
+		@return: The return value is an object -> set-of-sonames mapping, where
+			object is a broken binary and the set consists of sonames needed by
+			object that have no corresponding libraries to fulfill the dependency.
+
+		"""
+		class LibraryCache(object):
+
+			"""
+			Caches sonames and realpaths associated with paths.
+
+			The purpose of this class is to prevent multiple calls of
+			os.path.realpath and os.path.isfile on the same paths.
+
+			"""
+
+			def __init__(cache_self):
+				cache_self.cache = {}
+
+			def get(cache_self, path):
+				"""
+				Caches and returns the soname and realpath for a path.
+
+				@param path: absolute path (can be symlink)
+				@type path: string (example: '/usr/lib/libfoo.so')
+				@rtype: 3-tuple with types (string or None, string, boolean)
+				@return: 3-tuple with the following components:
+					1. soname as a string or None if it does not exist,
+					2. realpath as a string,
+					3. the result of os.path.isfile(realpath)
+					(example: ('libfoo.so.1', '/usr/lib/libfoo.so.1.5.1', True))
+
+				"""
+				if path in cache_self.cache:
+					return cache_self.cache[path]
+				else:
+					realpath = os.path.realpath(path)
+					# Check that the library exists on the filesystem.
+					if os.path.isfile(realpath):
+						# Get the soname from LinkageMap._obj_properties if it
+						# exists. Otherwise, None.
+						soname = self._obj_properties.get(realpath, (None,)*3)[3]
+						# Both path and realpath are cached and the result is
+						# returned.
+						cache_self.cache.setdefault(realpath, \
+								(soname, realpath, True))
+						return cache_self.cache.setdefault(path, \
+								(soname, realpath, True))
+					else:
+						# realpath is not cached here, because the majority of cases
+						# where realpath is not a file, path is the same as realpath.
+						# Thus storing twice slows down the cache performance.
+						return cache_self.cache.setdefault(path, \
+								(None, realpath, False))
+
+		debug = False
+		rValue = {}
+		cache = LibraryCache()
+		providers = self.listProviders()
+#		providers = self.listProvidersForReachableBinaries(self.getBinaries())
+
+		# Iterate over all binaries and their providers.
+		for obj, sonames in providers.items():
+			# Iterate over each needed soname and the set of library paths that
+			# fulfill the soname to determine if the dependency is broken.
+			for soname, libraries in sonames.items():
+				# validLibraries is used to store libraries, which satisfy soname,
+				# so if no valid libraries are found, the soname is not satisfied
+				# for obj.  Thus obj must be emerged.
+				validLibraries = set()
+				# It could be the case that the library to satisfy the soname is
+				# not in the obj's runpath, but a symlink to the library is (eg
+				# libnvidia-tls.so.1 in nvidia-drivers).  Also, since LinkageMap
+				# does not catalog symlinks, broken or missing symlinks may go
+				# unnoticed.  As a result of these cases, check that a file with
+				# the same name as the soname exists in obj's runpath.
+				path = self._obj_properties[obj][2] + self._defpath
+				for dir in path:
+					cachedSoname, cachedRealpath, cachedExists = \
+							cache.get(os.path.join(dir, soname))
+					# Check that the this library provides the needed soname.  Doing
+					# this, however, will cause consumers of libraries missing
+					# sonames to be unnecessarily emerged. (eg libmix.so)
+					if cachedSoname == soname:
+						validLibraries.add(cachedRealpath)
+						if debug and cachedRealpath not in libraries:
+							print "Found provider outside of findProviders:", \
+									os.path.join(dir, soname), "->", cachedRealpath
+						# A valid library has been found, so there is no need to
+						# continue.
+						break
+					if debug and cachedRealpath in self._obj_properties:
+						print "Broken symlink or missing/bad soname:", \
+								os.path.join(dir, soname), '->', cachedRealpath, \
+								"with soname", cachedSoname, "but expecting", soname
+				# This conditional checks if there are no libraries to satisfy the
+				# soname (empty set).
+				if not validLibraries:
+					rValue.setdefault(obj, set()).add(soname)
+					# If no valid libraries have been found by this point, then
+					# there are no files named with the soname within obj's runpath,
+					# but if there are libraries (from the providers mapping), it is
+					# likely that symlinks or the actual libraries are missing.
+					# Thus possible symlinks and missing libraries are added to
+					# rValue in order to emerge corrupt library packages.
+					for lib in libraries:
+						cachedSoname, cachedRealpath, cachedExists = cache.get(lib)
+						if not cachedExists:
+							# The library's package needs to be emerged to repair the
+							# missing library.
+							rValue.setdefault(lib, set()).add(soname)
+						else:
+							# A library providing the soname exists in the obj's
+							# runpath, but no file named as the soname exists, so add
+							# the path constructed from the lib's directory and the
+							# soname to rValue to fix cases of vanishing (or modified)
+							# symlinks.  This path is not guaranteed to exist, but it
+							# follows the symlink convention found in the majority of
+							# packages.
+							rValue.setdefault(os.path.join(os.path.dirname(lib), \
+									soname), set()).add(soname)
+						if debug:
+							if not cachedExists:
+								print "Missing library:", lib
+							else:
+								print "Possibly missing symlink:", \
+										os.path.join(os.path.dirname(lib), soname)
+
+		return rValue
+
+	def listProviders(self):
+		"""
+		Find the providers for all binaries.
+
+		@rtype: dict (example:
+			{'/usr/bin/foo': {'libbar.so': set(['/lib/libbar.so.1.5'])}})
+		@return: The return value is an object -> providers mapping, where
+			providers is a mapping of soname -> set-of-library-paths returned
+			from the findProviders method.
+
+		"""
+		rValue = {}
+		if not self._libs:
+			self.rebuild()
+		# Iterate over all binaries within LinkageMap.
+		for obj in self._obj_properties.keys():
+			rValue.setdefault(obj, self.findProviders(obj))
+		return rValue
+
 	def isMasterLink(self, obj):
 		basename = os.path.basename(obj)
 		if obj not in self._obj_properties:

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #4: sets.conf.2.2_rc6.patch --]
[-- Type: text/x-patch; name=sets.conf.2.2_rc6.patch, Size: 397 bytes --]

--- sets.conf.2.2_rc6	2008-08-08 13:50:04.000000000 -0500
+++ /usr/share/portage/config/sets.conf	2008-08-08 13:41:01.000000000 -0500
@@ -59,3 +59,9 @@
 [downgrade]
 class = portage.sets.dbapi.DowngradeSet
 world-candidate = False
+
+# Packages to rebuild broken library dependencies.
+[missing-rebuild]
+class = portage.sets.libs.MissingLibraryConsumerSet
+world-candidate = False
+debug = False

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [gentoo-soc] Progress Report - Revdep-rebuild
@ 2008-08-21  3:09 Lucian Poston
  2008-08-21 15:47 ` Donnie Berkholz
  0 siblings, 1 reply; 13+ messages in thread
From: Lucian Poston @ 2008-08-21  3:09 UTC (permalink / raw
  To: gentoo-soc; +Cc: Marius Mauch

[-- Attachment #1: Type: text/plain, Size: 3021 bytes --]

Last week's work was interesting.  As mentioned in the last report, I
significantly modified LinkageMap to utilize path's inodes rather
os.path.realpath for path comparisons -- basically, reducing the
amount of filesystem access.  It turned out to improve its efficiency,
which in turn improved the efficiency of MisingLibraryConsumerSet as
it relies on LinkageMap.listBrokenBinaries.

I previously mentioned there would be problems in cases where
LinkageMap is rebuilt when files have been removed but the /var/db/pkg
database has not been updated accordingly.  I got around this by
falling back on os.path.realpath for missing files (or more generally,
when os.stat fails).

For future endeavors, I will explore adding a new entry into
/var/db/pkg to catalog libtool libraries (similar in nature to
NEEDED.ELF.2).  Currently, the contents of all packages are searched
in order to find libtool libraries, so having them available in the
vdb_path will speed things up.

Here are the known problems:

1) I've noticed that on my older systems, I had to re-emerge several
packages, which appeared broken due older/incomplete NEEDED.ELF.2
entries.  They appeared to be missing entries for various files.  I
suppose this is due to modifications to the scripts that generate
these files as those systems had been using older versions of portage.
 Everything worked fine after updating these files.

2) In certain situations, packages may be ordered such that a second
emerge is necessary.  This is due to dependency neglection in portage.
[1]  The flag --complete-graph can be used to properly order packages
in these situations.

3) In the last report [2], I mentioned the functionality to emerge the
set of packages containing consumers of libraries matched by a regular
expression.  The libraries returned by LinkageMap.listLibraryObjects()
are matched against that regular expression.  Consequently, only files
cataloged in LinkageMap will be considered.  Since symlinks aren't
entered into NEEDED.ELF.2 files, LinkageMap doesn't catalog them.  So
if /usr/lib were a symlink to /usr/lib64 and the regular expression
were /usr/lib/libfoo*, nothing would be matched (libfoo would work
however).  This can be fixed by adding symlink entries into
LinkageMap, searching CONTENTS files for symlinks, or utilizing the
find utility.  I'll decide to search CONTENTS files to resolve this.

4) As masks are used to filter out specially evaluated binaries, there
is always the potential for false positives until the correct masks
are added.

Attached are three patches[3] for portage-2.2_rc8 for the files:
/usr/lib/portage/pym/portage/dbapi/vartree.py
/usr/lib/portage/pym/portage/sets/libs.py
/usr/share/portage/config/sets.conf

Lucian


[1] http://dev.gentoo.org/~zmedico/portage/doc/portage.html#dependency-resolution-package-modeling-dependency-neglection
[2] http://archives.gentoo.org/gentoo-soc/msg_b8a874ceac9b58c1badf86f3eaff4803.xml
[3] http://repo.or.cz/w/revdep-rebuild-reimplementation.git?a=shortlog;h=refs/heads/rc3

[-- Attachment #2: libs.py.2.2_rc8.patch --]
[-- Type: application/octet-stream, Size: 12013 bytes --]

--- libs.py.2.2_rc8	2008-08-14 15:45:30.000000000 -0500
+++ pym/portage/sets/libs.py	2008-08-18 04:25:32.000000000 -0500
@@ -2,10 +2,18 @@
 # Distributed under the terms of the GNU General Public License v2
 # $Id: libs.py 10759 2008-06-22 04:04:50Z zmedico $
 
+import os
+import re
+import time
+from portage.dbapi.vartree import dblink
+from portage.versions import catsplit
 from portage.sets.base import PackageSet
 from portage.sets import get_boolean
 from portage.versions import catpkgsplit
 
+__all__ = ["LibraryConsumerSet", "PreservedLibraryConsumerSet",
+		"MissingLibraryConsumerSet"]
+
 class LibraryConsumerSet(PackageSet):
 	_operations = ["merge", "unmerge"]
 
@@ -45,3 +53,324 @@
 		debug = get_boolean(options, "debug", False)
 		return PreservedLibraryConsumerSet(trees["vartree"].dbapi, debug)
 	singleBuilder = classmethod(singleBuilder)
+
+
+class MissingLibraryConsumerSet(LibraryConsumerSet):
+
+	"""
+	This class is the set of packages to emerge due to missing libraries.
+
+	This class scans binaries for missing and broken shared library dependencies
+	and fixes them by emerging the packages containing the broken binaries.
+
+	The user may also emerge packages containing consumers of specified
+	libraries by passing the name or a python regular expression through the
+	environment variable, LIBRARY.  Due to a limitation in passing flags to
+	package sets through the portage cli, the user must set environment
+	variables to modify the behaviour of this package set.  So if the
+	environment variable LIBRARY is set, the behaviour of this set changes.
+
+	"""
+
+	description = "The set of packages to emerge due to missing libraries."
+	_operations = ["merge"]
+
+	def __init__(self, vardbapi, debug=False):
+		super(MissingLibraryConsumerSet, self).__init__(vardbapi, debug)
+		# FIXME Since we can't get command line arguments from the user, the
+		# soname can be passed through an environment variable for now.
+		self.libraryRegexp = os.getenv("LIBRARY")
+		self.root = self.dbapi.root
+		self.linkmap = self.dbapi.linkmap
+
+	def load(self):
+		# brokenDependencies: object -> set-of-unsatisfied-dependencies, where
+		# object is an installed binary/library and
+		# set-of-unsatisfied-dependencies are sonames or libraries required by
+		# the object but have no corresponding libraries to fulfill the
+		# dependency.
+		brokenDependencies = {}
+		atoms = set()
+
+		# If the LIBRARY environment variable is set, the resulting package set
+		# will be packages containing consumers of the libraries matched by the
+		# variable.
+		if self.libraryRegexp:
+			atoms = self.findAtomsOfLibraryConsumers(self.libraryRegexp)
+			self._setAtoms(atoms)
+			if self.debug:
+				print
+				print "atoms to be emerged:"
+				for x in sorted(atoms):
+					print x
+			return
+
+		# Rebuild LinkageMap.
+		if self.debug:
+			timeStart = time.time()
+		self.linkmap.rebuild()
+		if self.debug:
+			timeRebuild = time.time() - timeStart
+
+		# Get the list of broken dependencies from LinkageMap.
+		if self.debug:
+			timeStart = time.time()
+		brokenDependencies = self.linkmap.listBrokenBinaries(self.debug)
+		if self.debug:
+			timeListBrokenBinaries = time.time() - timeStart
+
+		# Add broken libtool libraries into the brokenDependencies dict.
+		if self.debug:
+			timeStart = time.time()
+		brokenDependencies.update(self.listBrokenLibtoolLibraries())
+		if self.debug:
+			timeLibtool = time.time() - timeStart
+
+		# FIXME Too many atoms may be emerged because libraries in binary
+		# packages are not being handled properly eg openoffice, nvidia-drivers,
+		# sun-jdk.  Certain binaries are run in an environment where additional
+		# library paths are added via LD_LIBRARY_PATH.  Since these paths aren't
+		# registered in _obj_properties, they appear broken (and are if not run
+		# in the correct environment).  I have to determine if libraries and lib
+		# paths should be masked using /etc/revdep-rebuild/* as done in
+		# revdep-rebuild or if there is a better way to identify and deal with
+		# these problematic packages (or if something entirely different should
+		# be done).  For now directory and library masks are used.
+
+		# Remove masked directories and libraries.
+		if self.debug:
+			timeStart = time.time()
+		if brokenDependencies:
+			brokenDependencies = self.removeMaskedDependencies(brokenDependencies)
+		if self.debug:
+			timeMask = time.time() - timeStart
+
+		# Determine atoms to emerge based on broken objects in
+		# brokenDependencies.
+		if self.debug:
+			timeStart = time.time()
+		if brokenDependencies:
+			atoms = self.mapPathsToAtoms(set(brokenDependencies.keys()))
+		if self.debug:
+			timeAtoms = time.time() - timeStart
+
+		# Debug output
+		if self.debug:
+			print
+			print len(brokenDependencies), "brokenDependencies:"
+			for x in sorted(brokenDependencies.keys()):
+				print
+				print x, "->"
+				print '\t', brokenDependencies[x]
+			print
+			print "atoms to be emerged:"
+			for x in sorted(atoms):
+				print x
+			print
+			print "Rebuild time:", timeRebuild
+			print "Broken binaries time:", timeListBrokenBinaries
+			print "Broken libtool time:", timeLibtool
+			print "Remove mask time:", timeMask
+			print "mapPathsToAtoms time:", timeAtoms
+			print
+
+		self._setAtoms(atoms)
+
+	def removeMaskedDependencies(self, dependencies):
+		"""
+		Remove all masked dependencies and return the updated mapping.
+
+		@param dependencies: dependencies from which to removed masked
+			dependencies
+		@type dependencies: dict (example: {'/usr/bin/foo': set(['libfoo.so'])})
+		@rtype: dict
+		@return: shallow copy of dependencies with masked items removed
+
+		"""
+		rValue = dependencies.copy()
+		dirMask, libMask = self.getDependencyMasks()
+
+		# Remove entries that are masked.
+		if dirMask or libMask:
+			if self.debug:
+				print "The following are masked:"
+			for binary, libSet in rValue.items():
+				for directory in dirMask:
+					# Check if the broken binary lies within the masked directory or
+					# its subdirectories.
+					# XXX Perhaps we should allow regexps as masks.
+					if binary.startswith(directory):
+						del rValue[binary]
+						if self.debug:
+							print "dirMask:",binary
+						break
+				# Check if all the required libraries are masked.
+				if binary in rValue and libSet.issubset(libMask):
+					del rValue[binary]
+					if self.debug:
+						print "libMask:", binary, libSet & libMask
+
+		if self.debug:
+			print
+			print "Directory mask:", dirMask
+			print
+			print "Library mask:", libMask
+
+		return rValue
+
+	def getDependencyMasks(self):
+		"""
+		Return all dependency masks as a tuple.
+
+		@rtype: 2-tuple of sets of strings
+		@return: 2-tuple in which the first component is a set of directory
+			masks and the second component is a set of library masks
+
+		"""
+		dirMask = set()
+		libMask = set()
+		_dirMask_re = re.compile(r'SEARCH_DIRS_MASK\s*=\s*"([^"]*)"')
+		_libMask_re = re.compile(r'LD_LIBRARY_MASK\s*=\s*"([^"]*)"')
+		lines = []
+
+		# Reads the contents of /etc/revdep-rebuild/*
+		libMaskDir = os.path.join(self.root, "etc", "revdep-rebuild")
+		if os.path.exists(libMaskDir):
+			for file in os.listdir(libMaskDir):
+				try:
+					f = open(os.path.join(libMaskDir, file), "r")
+					try:
+						lines.extend(f.readlines())
+					finally:
+						f.close()
+				except IOError: # OSError?
+					continue
+			# The following parses SEARCH_DIRS_MASK and LD_LIBRARY_MASK variables
+			# from /etc/revdep-rebuild/*
+			for line in lines:
+				matchDir = _dirMask_re.match(line)
+				matchLib = _libMask_re.match(line)
+				if matchDir:
+					dirMask.update(set(matchDir.group(1).split()))
+				if matchLib:
+					libMask.update(set(matchLib.group(1).split()))
+
+		# These directories contain specially evaluated libraries.
+		# app-emulation/vmware-workstation-6.0.1.55017
+		dirMask.add('/opt/vmware/workstation/lib')
+		# app-emulation/vmware-server-console-1.0.6.91891
+		dirMask.add('/opt/vmware/server/console/lib')
+		# www-client/mozilla-firefox-2.0.0.15
+		dirMask.add('/usr/lib/mozilla-firefox/plugins')
+		dirMask.add('/usr/lib64/mozilla-firefox/plugins')
+		# app-office/openoffice-2.4.1
+		dirMask.add('/opt/OpenOffice')
+		dirMask.add('/usr/lib/openoffice')
+		# dev-libs/libmix-2.05  libmix.so is missing soname entry
+		libMask.add('libmix.so')
+		# app-accessibility/speech-tools-1.2.96_beta  missing sonames
+		libMask.add('libestools.so')
+		libMask.add('libestbase.so')
+		libMask.add('libeststring.so')
+		# app-emulation/emul-linux-x86-soundlibs-20080418
+		dirMask.add('/usr/kde/3.5/lib32')
+
+		return (dirMask, libMask)
+
+	def listBrokenLibtoolLibraries(self):
+		"""
+		Find broken libtool libraries and their missing dependencies.
+
+		@rtype: dict (example: {'/lib/libfoo.la': set(['/lib/libbar.la'])})
+		@return: The return value is a library -> set-of-libraries mapping, where
+			library is a broken library and the set consists of dependencies
+			needed by library that do not exist on the filesystem.
+
+		"""
+		rValue = {}
+		lines = []
+		dependencies = []
+		_la_re = re.compile(r".*\.la$")
+		_dependency_libs_re = re.compile(r"^dependency_libs\s*=\s*'(.*)'")
+
+		# Loop over the contents of all packages.
+		for cpv in self.dbapi.cpv_all():
+			mysplit = catsplit(cpv)
+			link = dblink(mysplit[0], mysplit[1], myroot=self.dbapi.root, \
+					mysettings=self.dbapi.settings, treetype='vartree', \
+					vartree=self.dbapi.vartree)
+			for file in link.getcontents():
+				# Check if the file ends with '.la'.
+				matchLib = _la_re.match(file)
+				if matchLib:
+					# Read the lines from the library.
+					lines = []
+					try:
+						f = open(file, "r")
+						try:
+							lines.extend(f.readlines())
+						finally:
+							f.close()
+					except IOError:
+						continue
+					# Find the line listing the dependencies.
+					for line in lines:
+						matchLine = _dependency_libs_re.match(line)
+						if matchLine:
+							dependencies = matchLine.group(1).split()
+							# For each dependency that is a pathname (begins with
+							# os.sep), check that it exists on the filesystem.  If it
+							# does not exist, then add the library and the missing
+							# dependency to rValue.
+							for dependency in dependencies:
+								if dependency[0] == os.sep and \
+										not os.path.isfile(dependency):
+									rValue.setdefault(file, set()).add(dependency)
+
+		return rValue
+
+	def findAtomsOfLibraryConsumers(self, searchString):
+		"""
+		Return atoms containing consumers of libraries matching the argument.
+
+		@param searchString: a string used to search for libraries
+		@type searchString: string to be compiled as a regular expression
+			(example: 'libfoo.*')
+		@rtype: set of strings
+		@return: the returned set of atoms are valid to be used by package sets
+
+		"""
+		atoms = set()
+		consumers = set()
+		matchedLibraries = set()
+		libraryObjects = self.linkmap.listLibraryObjects()
+		_librarySearch_re = re.compile(searchString)
+
+		# Find libraries matching searchString.
+		for library in libraryObjects:
+			m = _librarySearch_re.search(library)
+			if m:
+				matchedLibraries.add(library)
+				consumers.update(self.linkmap.findConsumers(library))
+
+		if self.debug:
+			print
+			print "Consumers of the following libraries will be emerged:"
+			for x in matchedLibraries:
+				print x
+
+		if consumers:
+			# The following prevents emerging the packages that own the matched
+			# libraries.  Note that this will prevent updating the packages owning
+			# the libraries if there are newer versions available in the installed
+			# slot.  See bug #30095
+			atoms = self.mapPathsToAtoms(consumers)
+			libraryOwners = self.mapPathsToAtoms(matchedLibraries)
+			atoms.difference_update(libraryOwners)
+
+		return atoms
+
+	def singleBuilder(self, options, settings, trees):
+		debug = get_boolean(options, "debug", False)
+		return MissingLibraryConsumerSet(trees["vartree"].dbapi, debug)
+	singleBuilder = classmethod(singleBuilder)

[-- Attachment #3: vartree.py.2.2_rc8.patch --]
[-- Type: application/octet-stream, Size: 21012 bytes --]

--- vartree.py.2.2_rc8	2008-08-17 21:11:41.000000000 -0500
+++ pym/portage/dbapi/vartree.py	2008-08-18 04:13:33.000000000 -0500
@@ -143,10 +143,12 @@
 		self._dbapi = vardbapi
 		self._libs = {}
 		self._obj_properties = {}
-		self._defpath = getlibpaths()
-	
+		self._defpath = set(getlibpaths())
+		self._obj_key_cache = {}
+
 	def rebuild(self, include_file=None):
 		libs = {}
+		obj_key_cache = {}
 		obj_properties = {}
 		lines = []
 		for cpv in self._dbapi.cpv_all():
@@ -176,29 +178,61 @@
 				# insufficient field length
 				continue
 			arch = fields[0]
-			obj = os.path.realpath(fields[1])
+			obj = fields[1]
+			obj_key = self._generateObjKey(obj)
 			soname = fields[2]
-			path = filter(None, fields[3].replace(
+			path = set([normalize_path(x)
+				for x in filter(None, fields[3].replace(
 				"${ORIGIN}", os.path.dirname(obj)).replace(
-				"$ORIGIN", os.path.dirname(obj)).split(":"))
+				"$ORIGIN", os.path.dirname(obj)).split(":"))])
 			needed = filter(None, fields[4].split(","))
 			if soname:
-				libs.setdefault(soname, {arch: {"providers": [], "consumers": []}})
-				libs[soname].setdefault(arch, {"providers": [], "consumers": []})
-				libs[soname][arch]["providers"].append(obj)
+				libs.setdefault(soname, \
+						{arch: {"providers": set(), "consumers": set()}})
+				libs[soname].setdefault(arch, \
+						{"providers": set(), "consumers": set()})
+				libs[soname][arch]["providers"].add(obj_key)
 			for x in needed:
-				libs.setdefault(x, {arch: {"providers": [], "consumers": []}})
-				libs[x].setdefault(arch, {"providers": [], "consumers": []})
-				libs[x][arch]["consumers"].append(obj)
-			obj_properties[obj] = (arch, needed, path, soname)
-		
+				libs.setdefault(x, \
+						{arch: {"providers": set(), "consumers": set()}})
+				libs[x].setdefault(arch, {"providers": set(), "consumers": set()})
+				libs[x][arch]["consumers"].add(obj_key)
+			obj_key_cache.setdefault(obj, obj_key)
+			# All object paths are added into the obj_properties tuple
+			obj_properties.setdefault(obj_key, \
+					(arch, needed, path, soname, set()))[4].add(obj)
+
 		self._libs = libs
 		self._obj_properties = obj_properties
+		self._obj_key_cache = obj_key_cache
 
-	def listBrokenBinaries(self):
+	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.
 
+		@param debug: Boolean to enable debug output
+		@type debug: Boolean
 		@rtype: dict (example: {'/usr/bin/foo': set(['libbar.so'])})
 		@return: The return value is an object -> set-of-sonames mapping, where
 			object is a broken binary and the set consists of sonames needed by
@@ -208,65 +242,66 @@
 		class LibraryCache(object):
 
 			"""
-			Caches sonames and realpaths associated with paths.
+			Caches properties associated with paths.
 
 			The purpose of this class is to prevent multiple calls of
-			os.path.realpath and os.path.isfile on the same paths.
+			_generateObjKey on the same paths.
 
 			"""
 
 			def __init__(cache_self):
 				cache_self.cache = {}
 
-			def get(cache_self, path):
+			def get(cache_self, obj):
 				"""
-				Caches and returns the soname and realpath for a path.
+				Caches and returns properties associated with an object.
 
-				@param path: absolute path (can be symlink)
-				@type path: string (example: '/usr/lib/libfoo.so')
-				@rtype: 3-tuple with types (string or None, string, boolean)
-				@return: 3-tuple with the following components:
-					1. soname as a string or None if it does not exist,
-					2. realpath as a string,
-					3. the result of os.path.isfile(realpath)
-					(example: ('libfoo.so.1', '/usr/lib/libfoo.so.1.5.1', True))
+				@param obj: absolute path (can be symlink)
+				@type obj: string (example: '/usr/lib/libfoo.so')
+				@rtype: 4-tuple with types
+					(string or None, string or None, 2-tuple, Boolean)
+				@return: 4-tuple with the following components:
+					1. arch as a string or None if it does not exist,
+					2. soname as a string or None if it does not exist,
+					3. obj_key as 2-tuple,
+					4. Boolean representing whether the object exists.
+					(example: ('libfoo.so.1', (123L, 456L), True))
 
 				"""
-				if path in cache_self.cache:
-					return cache_self.cache[path]
+				if obj in cache_self.cache:
+					return cache_self.cache[obj]
 				else:
-					realpath = os.path.realpath(path)
+					if obj in self._obj_key_cache:
+						obj_key = self._obj_key_cache.get(obj)
+					else:
+						obj_key = self._generateObjKey(obj)
 					# Check that the library exists on the filesystem.
-					if os.path.isfile(realpath):
-						# Get the soname from LinkageMap._obj_properties if it
-						# exists. Otherwise, None.
-						soname = self._obj_properties.get(realpath, (None,)*3)[3]
-						# Both path and realpath are cached and the result is
-						# returned.
-						cache_self.cache.setdefault(realpath, \
-								(soname, realpath, True))
-						return cache_self.cache.setdefault(path, \
-								(soname, realpath, True))
+					if isinstance(obj_key, tuple):
+						# Get the arch and soname from LinkageMap._obj_properties if
+						# it exists. Otherwise, None.
+						arch, _, _, soname, _ = \
+								self._obj_properties.get(obj_key, (None,)*5)
+						return cache_self.cache.setdefault(obj, \
+								(arch, soname, obj_key, True))
 					else:
-						# realpath is not cached here, because the majority of cases
-						# where realpath is not a file, path is the same as realpath.
-						# Thus storing twice slows down the cache performance.
-						return cache_self.cache.setdefault(path, \
-								(None, realpath, False))
+						return cache_self.cache.setdefault(obj, \
+								(None, None, obj_key, False))
 
-		debug = False
 		rValue = {}
 		cache = LibraryCache()
 		providers = self.listProviders()
 
-		# Iterate over all binaries and their providers.
-		for obj, sonames in providers.items():
+		# Iterate over all obj_keys and their providers.
+		for obj_key, sonames in providers.items():
+			arch, _, path, _, objs = self._obj_properties[obj_key]
+			path = path.union(self._defpath)
 			# Iterate over each needed soname and the set of library paths that
 			# fulfill the soname to determine if the dependency is broken.
 			for soname, libraries in sonames.items():
 				# validLibraries is used to store libraries, which satisfy soname,
 				# so if no valid libraries are found, the soname is not satisfied
-				# for obj.  Thus obj must be emerged.
+				# for obj_key.  If unsatisfied, objects associated with obj_key
+				# must be emerged.
 				validLibraries = set()
 				# It could be the case that the library to satisfy the soname is
 				# not in the obj's runpath, but a symlink to the library is (eg
@@ -274,67 +309,60 @@
 				# does not catalog symlinks, broken or missing symlinks may go
 				# unnoticed.  As a result of these cases, check that a file with
 				# the same name as the soname exists in obj's runpath.
-				path = self._obj_properties[obj][2] + self._defpath
-				for d in path:
-					cachedSoname, cachedRealpath, cachedExists = \
-							cache.get(os.path.join(d, soname))
-					# Check that the this library provides the needed soname.  Doing
+				# XXX If we catalog symlinks in LinkageMap, this could be improved.
+				for directory in path:
+					cachedArch, cachedSoname, cachedKey, cachedExists = \
+							cache.get(os.path.join(directory, soname))
+					# Check that this library provides the needed soname.  Doing
 					# this, however, will cause consumers of libraries missing
 					# sonames to be unnecessarily emerged. (eg libmix.so)
-					if cachedSoname == soname:
-						validLibraries.add(cachedRealpath)
-						if debug and cachedRealpath not in libraries:
+					if cachedSoname == soname and cachedArch == arch:
+						validLibraries.add(cachedKey)
+						if debug and cachedKey not in \
+								set(map(self._obj_key_cache.get, libraries)):
+							# XXX This is most often due to soname symlinks not in
+							# a library's directory.  We could catalog symlinks in
+							# LinkageMap to avoid checking for this edge case here.
 							print "Found provider outside of findProviders:", \
-									os.path.join(d, soname), "->", cachedRealpath
+									os.path.join(directory, soname), "->", \
+									self._obj_properties[cachedKey][4], libraries
 						# A valid library has been found, so there is no need to
 						# continue.
 						break
-					if debug and cachedRealpath in self._obj_properties:
+					if debug and cachedArch == arch and \
+							cachedKey in self._obj_properties:
 						print "Broken symlink or missing/bad soname:", \
-								os.path.join(d, soname), '->', cachedRealpath, \
-								"with soname", cachedSoname, "but expecting", soname
+								os.path.join(directory, soname), '->', \
+								self._obj_properties[cachedKey], "with soname", \
+								cachedSoname, "but expecting", soname
 				# This conditional checks if there are no libraries to satisfy the
 				# soname (empty set).
 				if not validLibraries:
-					rValue.setdefault(obj, set()).add(soname)
+					for obj in objs:
+						rValue.setdefault(obj, set()).add(soname)
 					# If no valid libraries have been found by this point, then
 					# there are no files named with the soname within obj's runpath,
 					# but if there are libraries (from the providers mapping), it is
-					# likely that symlinks or the actual libraries are missing.
-					# Thus possible symlinks and missing libraries are added to
-					# rValue in order to emerge corrupt library packages.
+					# likely that soname symlinks or the actual libraries are
+					# missing or broken.  Thus those libraries are added to rValue
+					# in order to emerge corrupt library packages.
 					for lib in libraries:
-						cachedSoname, cachedRealpath, cachedExists = cache.get(lib)
-						if not cachedExists:
-							# The library's package needs to be emerged to repair the
-							# missing library.
-							rValue.setdefault(lib, set()).add(soname)
-						else:
-							# A library providing the soname exists in the obj's
-							# runpath, but no file named as the soname exists, so add
-							# the path constructed from the lib's directory and the
-							# soname to rValue to fix cases of vanishing (or modified)
-							# symlinks.  This path is not guaranteed to exist, but it
-							# follows the symlink convention found in the majority of
-							# packages.
-							rValue.setdefault(os.path.join(os.path.dirname(lib), \
-									soname), set()).add(soname)
+						rValue.setdefault(lib, set()).add(soname)
 						if debug:
-							if not cachedExists:
+							if not os.path.isfile(lib):
 								print "Missing library:", lib
 							else:
 								print "Possibly missing symlink:", \
 										os.path.join(os.path.dirname(lib), soname)
-
 		return rValue
 
 	def listProviders(self):
 		"""
-		Find the providers for all binaries.
+		Find the providers for all object keys in LinkageMap.
 
 		@rtype: dict (example:
-			{'/usr/bin/foo': {'libbar.so': set(['/lib/libbar.so.1.5'])}})
-		@return: The return value is an object -> providers mapping, where
+			{(123L, 456L): {'libbar.so': set(['/lib/libbar.so.1.5'])}})
+		@return: The return value is an object key -> providers mapping, where
 			providers is a mapping of soname -> set-of-library-paths returned
 			from the findProviders method.
 
@@ -342,118 +370,188 @@
 		rValue = {}
 		if not self._libs:
 			self.rebuild()
-		# Iterate over all binaries within LinkageMap.
-		for obj in self._obj_properties:
-			rValue.setdefault(obj, self.findProviders(obj))
+		# Iterate over all object keys within LinkageMap.
+		for obj_key in self._obj_properties:
+			rValue.setdefault(obj_key, self.findProviders(obj_key=obj_key))
 		return rValue
 
 	def isMasterLink(self, obj):
+		"""
+		Determine whether an object is a master link.
+
+		@param obj: absolute path to an object
+		@type obj: string (example: '/usr/bin/foo')
+		@rtype: Boolean
+		@return:
+			1. True if obj is a master link
+			2. False if obj is not a master link
+
+		"""
 		basename = os.path.basename(obj)
-		if obj not in self._obj_properties:
-			obj = os.path.realpath(obj)
-			if obj not in self._obj_properties:
-				raise KeyError("%s not in object list" % obj)
-		soname = self._obj_properties[obj][3]
+		obj_key = self._generateObjKey(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]
 		return (len(basename) < len(soname))
-		
+
 	def listLibraryObjects(self):
+		"""
+		Return a list of library objects.
+
+		Known limitation: library objects lacking an soname are not included.
+
+		@rtype: list of strings
+		@return: list of paths to all providers
+
+		"""
 		rValue = []
 		if not self._libs:
 			self.rebuild()
 		for soname in self._libs:
 			for arch in self._libs[soname]:
-				rValue.extend(self._libs[soname][arch]["providers"])
+				for obj_key in self._libs[soname][arch]["providers"]:
+					rValue.extend(self._obj_properties[obj_key][4])
 		return rValue
 
 	def getSoname(self, obj):
+		"""
+		Return the soname associated with an object.
+
+		@param obj: absolute path to an object
+		@type obj: string (example: '/usr/bin/bar')
+		@rtype: string
+		@return: soname as a string
+
+		"""
 		if not self._libs:
 			self.rebuild()
-		if obj not in self._obj_properties:
-			obj = os.path.realpath(obj)
-			if obj not in self._obj_properties:
-				raise KeyError("%s not in object list" % obj)
-		arch, needed, path, soname = self._obj_properties[obj]
-		return soname
+		if obj not in self._obj_key_cache:
+			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):
+		"""
+		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.
+
+		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
+		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: 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.
+
+		"""
+		rValue = {}
 
-	def findProviders(self, obj):
 		if not self._libs:
 			self.rebuild()
 
-		realpath_cache = {}
-		def realpath(p):
-			real_path = realpath_cache.get(p)
-			if real_path is None:
-				real_path = os.path.realpath(p)
-				realpath_cache[p] = real_path
-			return real_path
-
-		rValue = {}
-		if obj not in self._obj_properties:
-			obj = realpath(obj)
-			if obj not in self._obj_properties:
-				raise KeyError("%s not in object list" % obj)
-		arch, needed, path, soname = self._obj_properties[obj]
-		path = path[:]
-		path.extend(self._defpath)
-		path = set(realpath(x) for x in path)
-		for x in needed:
-			rValue[x] = set()
-			if x not in self._libs or arch not in self._libs[x]:
-				continue
-			for y in self._libs[x][arch]["providers"]:
-				if x[0] == os.sep and realpath(x) == realpath(y):
-					rValue[x].add(y)
-				elif realpath(os.path.dirname(y)) in path:
-					rValue[x].add(y)
+		# Determine the obj_key from the arguments.
+		if obj is not None:
+			obj_key = self._obj_key_cache.get(obj)
+			if obj_key not in self._obj_properties:
+				obj_key = self._generateObjKey(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)
+		for soname in needed:
+			rValue[soname] = set()
+			if soname not in self._libs or arch not in self._libs[soname]:
+				continue
+			# For each potential provider of the soname, add it to rValue if it
+			# resides in the obj's runpath.
+			for provider_key in self._libs[soname][arch]["providers"]:
+				providers = self._obj_properties[provider_key][4]
+				for provider in providers:
+					if os.path.dirname(provider) in path:
+						rValue[soname].add(provider)
 		return rValue
-	
-	def findConsumers(self, obj):
+
+	def findConsumers(self, obj=None, obj_key=None):
+		"""
+		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.
+
+		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: )
+		@return: The return value is a soname -> set-of-library-paths, where
+		set-of-library-paths satisfy soname.
+
+		"""
+		rValue = set()
+
 		if not self._libs:
 			self.rebuild()
 
-		realpath_cache = {}
-		def realpath(p):
-			real_path = realpath_cache.get(p)
-			if real_path is None:
-				real_path = os.path.realpath(p)
-				realpath_cache[p] = real_path
-			return real_path
-
-		if obj not in self._obj_properties:
-			obj = realpath(obj)
-			if obj not in self._obj_properties:
-				raise KeyError("%s not in object list" % obj)
+		# Determine the obj_key and the set of objects matching the arguments.
+		if obj is not None:
+			objs = set([obj])
+			obj_key = self._obj_key_cache.get(obj)
+			if obj_key not in self._obj_properties:
+				obj_key = self._generateObjKey(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])
 
 		# If there is another version of this lib with the
 		# same soname and the master link points to that
 		# other version, this lib will be shadowed and won't
 		# have any consumers.
-		arch, needed, path, soname = self._obj_properties[obj]
-		obj_dir = os.path.dirname(obj)
-		master_link = os.path.join(obj_dir, soname)
-		try:
-			master_st = os.stat(master_link)
-			obj_st = os.stat(obj)
-		except OSError:
-			pass
-		else:
-			if (obj_st.st_dev, obj_st.st_ino) != \
-				(master_st.st_dev, master_st.st_ino):
-				return set()
-
-		rValue = set()
-		for soname in self._libs:
-			for arch in self._libs[soname]:
-				if obj in self._libs[soname][arch]["providers"]:
-					for x in self._libs[soname][arch]["consumers"]:
-						path = self._obj_properties[x][2]
-						path = [realpath(y) for y in path+self._defpath]
-						if soname[0] == os.sep and realpath(soname) == realpath(obj):
-							rValue.add(x)
-						elif realpath(obj_dir) in path:
-							rValue.add(x)
+		if obj is not None:
+			soname = self._obj_properties[obj_key][3]
+			obj_dir = os.path.dirname(obj)
+			master_link = os.path.join(obj_dir, soname)
+			try:
+				master_st = os.stat(master_link)
+				obj_st = os.stat(obj)
+			except OSError:
+				pass
+			else:
+				if (obj_st.st_dev, obj_st.st_ino) != \
+					(master_st.st_dev, master_st.st_ino):
+					return set()
+
+		arch, _, _, soname, _ = self._obj_properties[obj_key]
+		if soname in self._libs and arch in self._libs[soname]:
+			# For each potential consumer, add it to rValue if an object from the
+			# arguments resides in the consumer's runpath.
+			for consumer_key in self._libs[soname][arch]["consumers"]:
+				_, _, path, _, consumer_objs = \
+						self._obj_properties[consumer_key]
+				path = path.union(self._defpath)
+				if objs_dirs.intersection(path):
+					rValue.update(consumer_objs)
 		return rValue
-					
+
 class vardbapi(dbapi):
 
 	_excluded_dirs = ["CVS", "lost+found"]

[-- Attachment #4: sets.conf.2.2_rc8.patch --]
[-- Type: application/octet-stream, Size: 382 bytes --]

--- sets.conf.2.2_rc8	2008-08-14 15:46:20.000000000 -0500
+++ /usr/share/portage/config/sets.conf	2008-08-14 15:56:18.000000000 -0500
@@ -63,3 +63,8 @@
 [downgrade]
 class = portage.sets.dbapi.DowngradeSet
 world-candidate = False
+
+# Packages to rebuild broken library dependencies.
+[missing-rebuild]
+class = portage.sets.libs.MissingLibraryConsumerSet
+world-candidate = False

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [gentoo-soc] Progress Report - Revdep-rebuild
  2008-08-21  3:09 Lucian Poston
@ 2008-08-21 15:47 ` Donnie Berkholz
  2008-08-21 18:09   ` Lucian Poston
  0 siblings, 1 reply; 13+ messages in thread
From: Donnie Berkholz @ 2008-08-21 15:47 UTC (permalink / raw
  To: gentoo-soc; +Cc: Marius Mauch

[-- Attachment #1: Type: text/plain, Size: 1409 bytes --]

On 22:09 Wed 20 Aug     , Lucian Poston wrote:
> Last week's work was interesting.  As mentioned in the last report, I
> significantly modified LinkageMap to utilize path's inodes rather
> os.path.realpath for path comparisons -- basically, reducing the
> amount of filesystem access.  It turned out to improve its efficiency,
> which in turn improved the efficiency of MisingLibraryConsumerSet as
> it relies on LinkageMap.listBrokenBinaries.

I just encountered a problem recently and wondered whether your work 
might help it at all. The situation is uninstalling a library that 
system packages link against. The problem I hit involves 
sys-apps/shadow[USE=audit] and sys-process/audit, which I just hit on my 
own system, breaking /bin/login.

It seems like this could become part of the linkage map and thus give 
the user a warning, if it's not already -- I haven't been following this 
incredibly closely.

What do you think?

> For future endeavors, I will explore adding a new entry into
> /var/db/pkg to catalog libtool libraries (similar in nature to
> NEEDED.ELF.2).  Currently, the contents of all packages are searched
> in order to find libtool libraries, so having them available in the
> vdb_path will speed things up.

Ick, libtool libraries are the worst.

-- 
Thanks,
Donnie

Donnie Berkholz
Developer, Gentoo Linux
Blog: http://dberkholz.wordpress.com

[-- Attachment #2: Type: application/pgp-signature, Size: 197 bytes --]

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [gentoo-soc] Progress Report - Revdep-rebuild
  2008-08-21 15:47 ` Donnie Berkholz
@ 2008-08-21 18:09   ` Lucian Poston
  0 siblings, 0 replies; 13+ messages in thread
From: Lucian Poston @ 2008-08-21 18:09 UTC (permalink / raw
  To: gentoo-soc; +Cc: Marius Mauch

On Thu, Aug 21, 2008 at 10:47 AM, Donnie Berkholz <dberkholz@gentoo.org> wrote:
> I just encountered a problem recently and wondered whether your work
> might help it at all. The situation is uninstalling a library that
> system packages link against. The problem I hit involves
> sys-apps/shadow[USE=audit] and sys-process/audit, which I just hit on my
> own system, breaking /bin/login.
>
> It seems like this could become part of the linkage map and thus give
> the user a warning, if it's not already -- I haven't been following this
> incredibly closely.
>
> What do you think?

In situations where a package is updated resulting in a new ABI for a
library (eg suppose libaudit.so.0 were updated to libaudit.so.1 in a
newer version of sys-process/audit), preserved libs would keep the old
libraries around by adding them to the CONTENTS of the newer package
so that libaudit.so.0 is still available for binaries that link to it.
 This is done by finding consumers (linked binaries) of files
(libraries) that are being unmerged via calling
LinkageMap.findConsumers on files to be unmerged.  However, in the
situation you provided, the package is removed, so (and I'm assuming
here) the only problem is deciding what to do with those needed
libraries.  Since sys-process/audit is being removed, there is no good
candidate package that would own the files necessary to preserve the
libraries, but throwing a warning to the user is certainly feasible
using a similar method to find preserved libraries.

As for using MissingLibraryConsumerSet in this situation, it'd
probably be too slow to calculate the set after each unmerge since the
set checks dependencies of the entire system.  To give you an idea, on
my older laptop it takes about 20-30 seconds to calculate the set (and
more like 10s on subsequent runs due to vfs caching inodes and such).
So it'd be much quicker to simply find consumers of files to be
unmerged and give a warning if any are found as this won't involve
nearly as much filesystem access.

Now in the situation where needed libraries have already been
uninstalled and binaries are broken, MissingLibraryConsumerSet can be
used to emerge packages necessary to fix the state of the system... if
you can login, that is  :)

Lucian



^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2008-08-21 18:09 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-08-11 23:12 [gentoo-soc] Progress Report - Revdep-rebuild Lucian Poston
  -- strict thread matches above, loose matches on Subject: below --
2008-08-21  3:09 Lucian Poston
2008-08-21 15:47 ` Donnie Berkholz
2008-08-21 18:09   ` Lucian Poston
2008-08-01 21:24 Lucian Poston
2008-07-27  6:28 Lucian Poston
2008-07-20  9:23 Lucian Poston
2008-07-12  3:13 Lucian Poston
2008-06-26  1:30 Lucian Poston
2008-06-26 17:47 ` Marius Mauch
2008-06-15  3:55 Lucian Poston
2008-06-17 15:55 ` Marius Mauch
2008-06-15  3:34 Lucian Poston

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox