[NTLUG:Discuss] apt-get, rpm and library versioning (was: Debian)

Burton Strauss Burton_Strauss at comcast.net
Thu Jun 23 11:09:37 CDT 2005


First off, separate .rpm vs. .dpkg (RedHat vs. Debian package format) from
the tools used to access them.  For many of us (e.g. Fedora), rpm (the tool)
and apt-get (the tool) both actually access .rpm packages.

If you want to fight over whether .dpgm or .rpm is the 'better' format, go
to it.  But I'll file that under /dev/null...


So use apt-get.  And you will have better luck.   But that's all it is, luck
- the real problems are in the library versioning.  And the only cure for
that is education.


RPM heck or it's predecessor dll heck (and it's new iteration 'apt killed my
system' during an upgrade) is really all a symptom of the widespread misuse
of library versioning.   That's right, the root problem is a very simple one
- version control.  And it's RARELY the fault of the program you are trying
to update.  Rather the fault lies with the common libraries.



Don't think that apt solves this.  All apt-get does is scan the package
files (even if they're .rpm files) and figure out what OTHER upgrades are
needed and creates a theoretically workable order.  You could do this by
hand, it's just error prone.  And if the information in the package files is
in error or incomplete, you get nailed.  But the prescan should catch the
catch-22 upgrade:

Product A needs library B 1.1.1
Library B needs library C 2.21tuesday or later
Library C 2.22friday is installed, but requires library B 1.2.1.

apt won't let you update because the =library B 1.1.1 restriction would
break.  It might be wrong - and really should be >=library B 1.1.1, but apt
can't tell.  So it aborts.



How is library versioning supposed to work?  How does it 'work' in practice?
----------------------------------------------------------------------------


Say Product A needs specific functions exported by Library B.  

Good programming practice dictates that in the future, when you code some
better version of Library B's superfunction() it should be replaced by
superduperfunction(), and you should continue to provide superfunction() -
usually by making a reasonable assumption about the new parameter and coding
this:

void superfunction() {
  int newparm;
  superduperfunction(newparm);
}

As a developer, the question is 'how long do I have to support this old
sh*t'??   So eventually, the developer drops superfunction() ('after all,
NOBODY is really still using it...').

As a packager, what you don't know and can't tell is whether some future
version of library B will change that interface that product A depends upon.
And whether the change will matter.    It's in the future - you can't tell.
But in the real world, maybe it's just the parameter structure of
superfunction() that will change.  Or maybe it will change in a way that
breaks product A.  It's the future - I can't tell.

So product A sets it's requirement for library B version a.b.c.  That's the
one I know works.  And the trap is baited.

Those dependencies are coded in the .rpm, .dpkg, or any other package
format.  And that's what causes the 'heck'.


Interestingly, that's precisely what library versioning is supposed to
solve.  And the library creator mis-uses the interface versioning system!

Did you ever wonder why you see this in /usr/lib:

lrwxrwxrwx    1 root root       13 Jan 15 10:52 libpng12.so -> libpng12.so.0
lrwxrwxrwx    1 root root       19 Jan 15 10:51 libpng12.so.0 ->
libpng12.so.0.1.2.8
-rwxr-xr-x    1 root root   143960 Dec  6  2004 libpng12.so.0.1.2.8
lrwxrwxrwx    1 root root       11 Jan 15 10:52 libpng.so -> libpng.so.3
lrwxrwxrwx    1 root root       18 Jan 15 10:52 libpng.so.2 ->
libpng.so.2.1.0.18
-rwxr-xr-x    1 root root   139256 Dec  6  2004 libpng.so.2.1.0.18
lrwxrwxrwx    1 root root       17 Jan 15 10:51 libpng.so.3 ->
libpng.so.3.1.2.8
lrwxrwxrwx    1 root root       19 Jan 15 10:51 libpng.so.3.1.2.8 ->
libpng12.so.0.1.2.8

libpng.so and/or libpng12.so are the version you are supposed to use if you
don't care about version.  But that's actually WRONG.

All of the others are specific versions of the interface (NOT of the
program, but most of us get this wrong).

libpng12.so.0.1.2.8 is version 0.1.2.8 of the libpng12 interface.
libpng12.so.0 is version 0 of the libpng12 interface.  It points at the
specific (detailed) 0.1.2.8 version, but there could also be many others
(0.1.2.7, 0.1.3, 0.1.2, etc.)

You are supposed to use libpng12.so.0 meaning - I want something in the v0
version.  I realize v1 will probably be different, but I don't care.

Just as you would use libpng12.so meaning - I don't care at all.

What it should be is this:

libpng.so.0.8, say, representing the 1.0.18 source version, but the ninth
version of the .0 interface.

Now libpng.1.2 represents such a drastic change that no code written for 1.0
will work, so you increment the major number of the interface.

And thus libpng.so.1.3, say, represents the 4th version of the .1 interface.
It's also the 1.2.8 source version.  This means that while there have been 8
versions of 1.2, only four have involved interface (called function)
changes.

You set this up by the following links:

libpng.so.0 -> libpng.so.0.8
libpng.so.1 -> libpng.so.1.3
libpng.so -> libpng.so.1

A program which just needs generic libpng, links to libpng.so.  One that
needs the specific libpng.so.0 interface links to that - it gets whichever
version of libpng.so.0 is installed, but that's good enough.  etc.

Unfortunately this makes packaging difficult - you need to depend on library
producers managing the versioning properly.  And, most don't - myself
included.  So you end up with RH creating libpng12 as a separate 'package'
so that you can specify the dependencies as 1.0 vs. 1.2...

Ugly, but it pretty much does work.  And so we perpetuate the problems into
a new generation of packaging tools and libraries and products.



-----Burton














More information about the Discuss mailing list