Home Wiki > User:Mvyskocil/Shared library packaging policy
Sign up | Login

User:Mvyskocil/Shared library packaging policy

tagline: From openSUSE


This policy is to provide naming conventions for packages containing shared libraries in %_libdir (or %_lib). The main rationale is to allow for two (incompatible) versions of a shared library packaged as libfoo1 and libfoo2 installed at the same time without causing RPM conflicts.

Rationale

All runtime libraries must be packaged in a package named according the library SONAME (will be explained later). This scheme allows parallel usage and installation of multiple versions of the library on a system without having a conflicts between packages. Packages with does not follow this policy are prohibited and won't be accepted to openSUSE Factory project.

An example

Let say we have to package zlib - one of the most common libraries ever - according shared library policy. We know the zlib is famous project name and the tarball is zlib-1.2.7.tar.bz2, so the most reasonable name of the **source** package is zlib.

 # Snippet of zlib.spec
 Name:     zlib
 Version:  1.2.7
 Source0:  http://zlib.net/zlib-%{version}.tar.bz2
 
 %package -n libz1
 zlib compression library ...
 
 %files -n libz1
 %defattr(-,root,root)
 %doc LICENSE
 %{_lib}/libz.so.*
 
 %package devel
 Requires: libz1 = %{version}
 Devel files for zlib compression library ...
   
 %files devel
 %defattr(-,root,root)
 %doc README ChangeLog
 %{_mandir}/man3/zlib.3.gz
 %{_includedir}/*.h
 %{_libdir}/libz.so
 %{_libdir}/pkgconfig/zlib.pc

In this case, the shared library is packaged in libz1 package, because the SONAME is libz.so.1 - see the #Package_naming for details. The include files and headers are stored in zlib-devel and there will be no package called zlib.rpm, because there are no files belongs to it.

   **TODO**: what about libz1-devel? I'd say in a case we don't want to maintain more than one version in Factory, it'd be zlib-devel.

Note: the versioned package is not intended to be manually installed by the used and correct rpm group is System/Libraries.

Other examples

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

Package naming

As has been said, the package name must be done according library's SONAME. It can be queried using objdump, or readelf

 $ objdump -x /lib64/libz.so.1 | grep SONAME
 SONAME               libz.so.1
 $ readelf -a /lib64/libz.so.1 | grep SONAME
  0x000000000000000e (SONAME)             Library soname: [libz.so.1]

So the SONAME is libz.so.1 (it does not need to be same as file name, especially when library is not built using libtool). The soname has the fixed structure lib${library name}(-${package version})?.so(-${SO version})?. The package version and SO version are optional.

Our library has the library-name z, no package-version, and so-version 1. Then the package name 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:

SONAME Versioned package name in distro Description
libz.so.1 libz1 With SO version
libdb-4.8.so libdb-4_8 With package version
libbz2.so.1 libbz2-1 Name with number and SO version
libzziplib-0.so.13 libzziplib-0-13 With version and SO version
libgame2-1.9.so.10.0.0 libgame2-1_9-10_0_0 All together and long SO number
libdsocks.so (shlib packaging not applicable) Nothing

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.

-devel packages

Devel packages (with suffix -devel) should be named according main package and omit the version number, because devel files usually conflicts, but there is usually no need to have them installed in paralel. Those packages includes things needed to build software based on the library.

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, or developer documentation (if the main package is almost empty).

And the -devel package must require the versioned package and any other -devel package, which is needed for the build.

There are two major exceptions from the naming rule

  • 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 API version of the library (usually this is the same number as the directory layout in /usr/share/include/). So such a -devel package would be named $NAME$API-devel.
  • 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.

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 not generate any shared libraries to start with, 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, 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.

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.

  **TODO**: this might be rewritten ...

Package content

  • Versioned package can contain shared library, libtool symlinks and. The license file have to be added too, especially for (L)GPL libraries - the directive %doc LICENSE will put it to the versioned directory. On the other hand, LICENSE might be ommited from the main or other subpackage, if it requires the versioned one.
  • All other content has to be approved and installed into versioned directory (/usr/share/libz1/foo), but it is better to create a required package providing the needed content.
  • A versioned package is allowed to ship multiple library files, if they share the same numbers and these numbers always change in lockstep throughout. (Example: libqt4 package shipping libQtCore.so.4, libQtNetwork.so.4 etc.)

What does not belongs to versioned package

  • 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 can be placed into the same package as the main RPM. (Example: encfs package as of openSUSE 12.1)

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/openSUSE 11.1), the ${pkglibexecdir} variable is available for this purpose.

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.

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 %makeinstall. 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

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.