openSUSE:Shared library packaging policy
tagline: From openSUSE
Build Service Tutorial · Tips & Tricks · Cross Distribution Howto · Packaging checks
Desktop menu categories · RPM Macros · Scriptlets · Init scripts · How to write good changes
Contents |
Rationale
That scheme makes it possible to install and use multiple shared libraries of the same base name, but different so-version, e.g. of older distribution in case there are programs requiring them. A discriminator needs to be part of the package name, as otherwise, the update stack will be confused, and using some monotonically increasing number as that makes sense. A strict structure on package names will also help those users writing quick & dirty tools.
From that follows that only files should be included therein which are not generating file conflicts later if installed together with another libfoo*.rpm. Hence only lib*.so.* files are allowed in them. To ensure that no shared libraries creep in which are not handled, we disallow them to be in any package not named via these rules.
Effectively, this creates a partition of all files into shared libs and others, and makes sure that no rpm contains files from both partitions.
Versioning schemes
The principle rule of versioning is that removing or changing the ABI in an incompatible way requires a new, different SONAME.
$ readelf -a /usr/lib64/libHX.so | grep SONAME 0x000000000000000e (SONAME) Library soname: [libHX.so.28]
When there is no SONAME entry, the filename itself is taken as the SONAME.
Using libtool
Many packages choose to follow libtool's concept of so-called interfaces and interface numbers, as described in the libtool manual. Such can be recognized by the use of -version-info and/or -release flags in Makefiles. As a developer and/or packager, note that the version info may be encoded differently (but still consistently) on different platforms, e.g.
| -version-info c:r:a | First int'f | Last int'f | Linux | FreeBSD |
|---|---|---|---|---|
| 7:0:0 | 7 | 7 | .so.7.0.0 | .so.7 |
| 8:0:1 | 7 | 8 | .so.7.1.0 | .so.8 |
| 9:0:2 | 7 | 9 | .so.7.2.0 | .so.9 |
| 9:0:1 | 8 | 9 | .so.8.1.0 | .so.9 |
| 9:0:0 | 9 | 9 | .so.9.0.0 | .so.9 |
It is therefore an error to attempt to tweak the c:r:a value in any way to get a particular SO number, and reeks of not having understood the interface concept.
Manual override
A few source packages want to manually define the SO number directly. Packages that wish to do so can use libtool's -version-number. They should not use -version-info in this case, to avoid being accused of having made the inevitable error of setting and/or changing c:r:a in obscure ways.
While the libtool manual kind of discourages the use of this option (quote: “New projects should use the -version-info flag instead.”), it does not mean the option is deprecated; it just wants everybody to be portable of course.
When there is no versioning
Some source packages may be in a state such that one or more of the following statements apply:
- the source may not generate any shared libraries to start with, and the distro packager intends to generate them on his own
- upstream may not care about versioning, i.e. producing a library that has the same SONAME (or no SONAME) despite ABI changes through different releases
- upstream forgot to update their numbers for a particular release
In such cases, as the libtool manual suggests, the safest course of action is to add the package version into the name (this can be done with or without libtool, BTW). libfoo.so shall then become libfoo-2.5.0.so (if the package version was 2.5.0). Prominent examples of this scheme is binutils's libbfd (/usr/lib(64)/libbfd*.so).
This scheme requires no coordination with any other parties while being universal and portable across Linux distributions.
Package naming
A library's name, specifically its SONAME, which is used as a base to construct the versioned package name, can be composed of multiple parts (though not all are required). Looking at a full-featured example, a filename like libfoo-bar-2.5.so.8.2.1 contains:
- general system prefix lib
- name of the library: foo-bar
- package version (optional): -2.5
- general system suffix for shared libraries: .so
- SO version (optional): 8.2.1
The distropackage's name is derived from the filename, and is constructed by concatenating:
- the lib prefix and the name, dots are replaced by underscores
- if an upstream version is specified, dots are replaced by underscores
- to avoid ambiguities, a dash is inserted between two numbers
- the SO version from the SONAME (which may be shorter than the filename), dots are replaced by underscores
All possibilities, with examples mostly taken from openSUSE:
| Description | SONAME | Versioned package name in distro |
|---|---|---|
| Nothing | libdsocks.so | (shlib packaging not applicable) |
| With package version | libdb-4.8.so | libdb-4_8 |
| With SO version | libblkid.so.1 | libblkid1 |
| Name with number and SO version | libbz2.so.1 | libbz2-1 |
| With version and SO version | libzziplib-0.so.13 | libzziplib-0-13 |
| All together and long SO number | libgame2-1.9.so.10.0.0 | libgame2-1_9-10_0_0 |
N.B.: The astute reader might have noticed that this mapping is not bijective (1:1). The hypothetical filenames libgame3.so.1 and libgame3-1.so would indeed lead to the same shlib package name (libgame3-1). This is not catered for by this policy, but such a situation has yet to occur in practice. Were such to occur, we would probably choose to patch the second package to produce libgame3-1.so.0 in place of libgame3-1.so to resolve the disambiguity.
Unversioned packages
Versioned packages are so called because they include all or part of a version in their name. The antipole to these are unversioned shlib packages for which most of this document does not apply. Examples of such packages are: bind-libs, cups-libs. Ever since the shlib policy was formalized, we seek to avoid or eliminate unversioned shlib packages within the boundaries of common sense — and gut feeling of when to better keep unversioned packages.
Eligibility
(This is a rewrite attempt of the "Package Contents" section in part, in a simpler fashion, using the exclusion principle.)
- If the shared library in question is a plugin (“module” in libtool lingo), the shlib policy does not apply — but do see the hints in the “Plugins” section below.
- If there is no corresponding -devel package for the shared library, the library should be placed into the same package as the main RPM. (Example: encfs package as of openSUSE 12.1)
Package Contents
- In general, versioned packages shall not contain anything but the shared library/libraries. No headers, no devel .so symlink, no config files, no documentation, etc.
- It may contain the license file if so required, and it must be in a versioned directory. (Using "%doc LICENSE" in a specfile's files section usually suffices.)
- If a versioned shared library package must contain other files than shared libraries for whatever (approved!) reason, their names must be made unambiguous by putting them in a versioned directory or by versioning their names to not conflict with the rationale of this policy.
- The (simpler) alternative: have the shlib package require another package (can be unversioned) for such files, if so supported.
- A versioned package is allowed to ship multiple library files, provided they share the same numbers and these numbers always change in lockstep throughout. (Example: libqt4 package shipping libQtCore.so.4, libQtNetwork.so.4 etc.)
- If one of the contained libraries does not create a dependency on all or most other contained libraries, then it is preferred to not merge those libraries into one rpm, but leave them in their own rpm. Consider installation size.
- All unsuffixed packages named lib* end with $NUM
- Common suffixes include e.g. -devel or -debuginfo
- Packages with suffix -devel should in general omit $NUM as -devel packages for different library versions usually conflict due to common header file names. See (4a) and (4b) for how it works otherwise.
- Files needed to develop programs using shared libraries contained in lib$NAME$NUM.rpm are packaged in a -devel package (see (4a) and (4b) for cases that need to version this package). Those files include lib*.so, lib*.la and all headers. Optionally those files can also be placed in $NAME.rpm, in the case that it also comes with other tools or documentation. But if there is a *-devel.rpm package, then it contains all lib*.so, lib*.la and headers. The -devel package is also an appropriate place to put license files.
- lib$NAME$NUM.rpm should not be needed to be manually installed by any user. The correct rpm group for such packages i System/Libraries
Best Practices
The following are guidelines for library packaging in general:
- Avoid packaging static libraries. You should use --disable-static configure option or, as a last resort, remove static libraries after make install. If in doubt, ask.
- If you package a static library in addition to a shared library, the static library should not be built with -fPIC.
- If you package a static library without a corresponding shared library, the static library must be built with -fPIC using --with-pic configure option if available.
- Avoid packaging libtool config files (.la files). In general they are not needed if you do not package a static library and put the .so file in a system directory (i.e. %_lib, /usr/%_lib). If your shared library resides — for whatever reason — in, for example, /opt/package/lib/libfoo.so.7, you do need the libfoo.la file, or further programs using libtool to link against libfoo.so.7 will be lacking the correct path.
- If in doubt, ask.
- Shared libraries are normally not standalone runnable (exception: libc.so.6 and ld-linux.so.2). But they often have the +x bit set!
Exceptions
- (2) A list of packages that are exempt from this policy is: (to be extended with explicit approval only)
- glibc
- pam
- (4a) If more than one version of a library is available from a single source repository, even conflicting -devel packages need to be suffixed to avoid multiple packages with the same package name. Proper conflicts need to be added in this case as well.
- (4b) If more than one version of a -devel package can be installed at the same time (for example because includes are packaged in a versioned directory and shared libraries have a versioned name like libgtk1.so.1), the -devel packages should be suffixed with a number that allows identifying the version of the library (usually this is the same number as the shared library package suffix $NUM). So such a -devel package would be named lib$NAME$NUM-devel.
Plugins
Plugins in the form of a shared library should be in a subdirectory in %_libdir appropriate for the program. (Example: %_libdir/apache2/mod_alias.so).
In automake (since 0.25), the ${pkglibdir} variable is available for this purpose. It defaults to ${libdir}/${PACKAGE} and therefore may need to be overriden if the plugin is shipped in a tarball separate from the program's main source.
Plugins in the form of executables should be in a subdirectory in %_libexecdir appropriate for the program. (Example: %_libexecdir/cups/filter/pdftops).
In automake (since 1.10b), the ${pkglibexecdir} variable is available for this purpose.
Hints
You should remember the following while trying to conform to this policy:
- There is no reason why a -devel package cannot provide development files for multiple shared library packages.
- There is no need to have a binary package that matches the source package name. In fact, you should avoid renaming the source package for different versions as we prefer to only have a single package version in the source repository.
- Dependencies on the shared library package result either from automatic dependencies created by rpm on the shared libraries if they are used, or from the single other case, the library -devel package, which should require the shared library package with a properly versioned requires.
- This policy and its enforcement only is about shared library packages and shared libraries placed in /%_lib or /usr/%_lib. Rules that apply to other parts are only best practices and policies for them may exist elsewhere.
Examples
The simplest example is a source package which builds a single shared library and development files (the zlib package matches this example). The resulting binary packages should be named libz1 and libz-devel with the following contents (file list shortened),
libz1:
/lib/libz.so.1 /lib/libz.so.1.2.3
libz-devel:
/usr/lib/libz.so /usr/lib/libz.a /usr/include/zlib.h /usr/share/man/man3/zlib.3.gz /usr/share/doc/packages/zlib/algorithm.txt
A more complicated example is if apart from the library and the development files there are also executables and documentation (the bzip2 package matches this example). There should be four packages (file list shortened),
libbz2-1:
/lib/libbz2.so.1 /lib/libbz2.so.1.0.0
libbz2-devel:
/usr/include/bzlib.h /usr/lib/libbz2.so /usr/lib/libbz2.a
bzip2:
/usr/bin/bzip2 /usr/share/man/man1/bzip2.1.gz
bzip2-doc:
/usr/share/doc/packages/bzip2-doc/manual.ps.gz
where documentation may be either merged into the binary package bzip2 or the development package libbz2-devel, if it is sufficiently small and related.
Another example is a library which depends on configuration or data files that can be shared with other versions of the library (the curl package matches this example). In this case, the config or data files should be split into a separate package and the shared library package should require that. For example, curl has the four packages libcurl4, libcurl-devel, curl and curl-ca-bundle (CA certificates required by libcurl4).
