Home Wiki > openSUSE:Shared library packaging policy
Sign up | Login

openSUSE:Shared library packaging policy

tagline: From openSUSE


These guidelines are to provide naming conventions for packages containing shared libraries, usually found in /%_lib or /usr/%_lib (%_libdir). The main rationale is to allow for two or more (incompatible) versions of a shared library, such as libfoo.so.1 and libfoo.so.2 to be independently selectively installable (and trackable by rpm) at the same time without causing RPM conflicts.

Rationale

This scheme makes it possible to install with the RPM package manager and use multiple shared libraries that share the same name stem but having different SO version (shared object version). Traditionally (pre-10.3), only one particular version of a library was built and shipped, and when the SO version changed, all packages depending on it also needed updating. This becomes a problem especially with packages coming from sources other than the distribution itself, since updated versions may not have been made available yet.

With the following naming and packaging guidelines, each library version becomes a separate installable entity, which allows for their coexistence.

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 package 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.

There are no restrictions for SO versions (and SONAME in general). You will often see single or triple integers, as in libfoo.so.1 and libfoo.so.1.0.0, however, there is no restriction that this scheme must be followed by the software developer. It is technically valid to use arbitrary characters and notations.

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 Windows
7:0:0 7 7 libfoo.so.7.0.0 libfoo.so.7 libfoo-7.dll
8:0:1 7 8 libfoo.so.7.1.0 libfoo.so.8 libfoo-8.dll
9:0:2 7 9 libfoo.so.7.2.0 libfoo.so.9 libfoo-9.dll
9:0:1 8 9 libfoo.so.8.1.0 libfoo.so.9 libfoo-9.dll
9:0:0 9 9 libfoo.so.9.0.0 libfoo.so.9 libfoo-9.dll

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:

  1. the source may totally lack support for building shared libraries, and the distro packager intends to generate them on his own
  2. 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
  3. 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, by the way). 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.

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 that has a 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.

Layout of the spec file

The shared library installables are usually a subpackage of a greater .spec file. The Build Service package name and .spec file name generally follow the name of the tarball. For the example of SDL2_image and libHX, it looks like:

Name: SDL2_image
...
%package -n libSDL2_image-2_0-0
...
%package devel
...
Name: libHX
...
%package -n libHX28
...
%package devel
...

Eligibility

  • If the shared library in question is a plugin (“module” in libtool nomenclature), 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, and the shlib policy does not apply. (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. (Example: the libnl3-200 package has a /usr/lib64/libnl3-200 directory.)
    • 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 it is available.
  • Avoid packaging libtool config files (.la files). If you do not package a static library, and if you are also placing the shared library in a standard search directory (i.e. %_lib, /usr/%_lib), they will not be needed. 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 source package should be names according upstream tarball (zlib, see Naming guidelines and Layout of the specfile) and resulting binary package has to be named libz1 and zlib-devel (libz-devel would be acceptable). You can omit the %files section for main package, therefor no zlib.rpm will be generated.

zlib.spec:

Name: zlib
...
%package -n libz1
%package devel
with the following contents (file list shortened),

libz1:

/lib/libz.so.1
/lib/libz.so.1.2.3

zlib-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).

Windows/MinGW packages

Everything what has been mentioned above can also be readily applied to packages containing Dynamic Link Libraries, Windows's implementation of shared libraries. This is mostly relevant for the projects rooted in the "windows" project on build.opensuse.org. The highlights:

  • DLLs are placed in /usr/platform/sys-root/mingw/bin.
  • The SONAME for a library is usually in the form of libfoo-2.dll. The composition is:
    • general system prefix lib
    • name of the library foo-bar
    • package version (optional): -2.5
    • SO version (optional): -8
    • system suffix for shared libraries (required): .dll
  • Like on the BSD platform(s), the libtool naming for Windows DLLs only contains the major number. (see table above)

Name transformation matrix:

Description SONAME Versioned package name in distro
Nothing libdsocks.dll (shlib packaging not applicable)
With package version libdb-4.8.dll libdb-4_8
With SO version libblkid-1.dll libblkid1
Name that has a number, and SO version libbz2-1.dll libbz2-1
With version and SO version libzziplib-0-13.dll libzziplib-0-13
All together and long SO number libgame2-1.9-10.0.0.dll libgame2-1_9-10_0_0

Projects may decide to prepend the mingw{32,64} identifier into the package name to avoid conflicts with native packages. You will therefore see names like mingw64-libHX28, for example.

Some libraries are built without the use of libtool, which may make it ambiguous at times as to which part constitutes the -release number and the -version-info. Examples and their resolutions:

  • libmwaw-0.1.dll (libmwaw-0.1.so.1 on Linux); given the corresponding Linux name, "libmwaw-0.1" is the clear basename, and the DLL is unversioned. The package name is therefore libmwaw-0_1.
  • libnettle-4-6.dll (libnettle.so.4 on Linux); while dashes are technically possible in SONAMEs, they are practically non-existant. Chose /-(\d+).dll/ for SO number, making the base name "libnettle-4". The package name is therefore libnettle-4-6, rather than libnettle4-6.