I am making a Debian package for a library, I'll call it libmystuff. It's currently at version 4.0.0, the next release will be 4.1.0 and will probably break API compatibility. The project uses CMake to build.
How should this be handled in the soname and package name?
I want to make the package version initially 4.0.0. If I name the package libmystuff, then I get a lintian error telling me to put the soname of the package in the package name (package-name-doesnt-match-sonames). Fair enough.
If I make the package name libmystuff4, then I get a package file named libmystuff4-dev_4.0.0-1ubuntu8_amd64.deb
, which seems slightly redundant but ok, the package is for major version 4 and the software is version 4.0.0. But I still get a lintian error saying,
libmystuff4: package-name-doesnt-match-sonames libmystuff4.0.0
So this is surprising to me, why does lintian want me to put all 3 parts of the soname in the package name, instead of just the first part?
In any case, so I change the package name to libmystuff4.0.0, and now lintian is quiet, but I get a package file called libmystuff4.0.0_4.0.0-1ubuntu8_amd64.deb
, which seems ultra redundant!
What should I do?
I'm thinking maybe the soname should be 0 even if the library version is 4.0.0, and I should set soname to 1 when they release 4.1.0, etc. This would require patching the upstream CMake build system, is that an acceptable approach? Although in that case what about the other parts of the soname, do I just set them to 0? Then the package would be soname 0.0.0.
Otherwise I'd have to change the soname to 5.0.0 when they release 4.1.0, which would get very confusing, right?
The short answer: set the current SONAME to libmystuff.so.0, and when you break ABI in 4.1.0, set the SONAME to libmystuff.so.1. You'll need to patch the buildsystem and introduce
set_property(TARGET mystuff PROPERTY SOVERSION 0 )
in the relevant place.Your SONAME is not the same as your library's version, and may have to evolve independently.
Longer answer: Libraries are a bit awkward in that they have two separate facets that can usefully be versioned: source compatibility, and binary compatibility. These are API and ABI respectively. A library release can maintain API and be source compatible while breaking ABI and being binary incompatible, or visa versa.
Fortunately, the only part that needs to be automated is the runtime binary compatibility. You can tell humans to read your documentation that the API broke between version 4.0.0 and version 4.1.0; you can't tell the runtime dynamic linker to read your documentation.
Thus the
SONAME
is born. Any dynamic object linked to your library has this string embedded in it, and it tells the dynamic linker what library file to load to resolve symbols from.Because it's a string there's basically no requirements on it - or information encoded in it. The runtime linker won't interpret it at all; it is only concerned with strict string equality.
This is where the Lintian warning is coming from - there is no “first part of the soname”; your SONAME is the string
libmystuff.so.4.0.0
. The 4.0.0 part is meaningful for humans, not the linker.As the SONAME is essentially arbitrary, conventions have grown up around it, and the convention is that the SONAME of the first version of
libmystuff
should belibmystuff.so.0
, and then the ‘.0’ should be increased by 1 each time a backwards-incompatible change to the ABI is made. So the first version islibmystuff.so.0
, the second ABI islibmystuff.so.1
, the third ABI islibmystuff.so.2
and so on.This is entirely independent of the library version - for example, the glibc project is currently at version 2.24 and produces a library with SONAME
libc.so.6
(and has done for over two decades now).If you use the project's version as the SONAME then every time you change the version anything using the library has to be rebuilt in order to use the new library. A program built against version 4.0.0 will have the string
libmystuff.so.4.0.0
embedded, and will not try to loadlibmystuff.so.4.0.1
.