Packaging/Shared Library Packaging Policy

From openSUSE

Contents

Definitions

- lib$NAME.so.* - a shared library, programs can depend on them, usually two files exist:

lib$NAME.so.$MAJOR
lib$NAME.so.$MAJOR.$MINOR.$MICRO

where lib$NAME.so.$MAJOR is a symlink to lib$NAME.so.$MAJOR.$MINOR.$MICRO

- SONAME - the name (query by objdump -x libfoo.so.1 | grep SONAME) a shared library is refered to by the dynamic loader. This matches the name of the shorter symlink lib$NAME.so.$MAJOR.

- lib$NAME.so - file used by the link editor (ld) to link executables, usually a softlink to the shared library file

- lib$NAME.a - a static library, used by the link editor (ld) to statically link executables

- lib$NAME.la - a libtool config file used for static linking with lib$NAME and dynamic loading with the libtool dynamic loader ltdl


Shared Library Naming and Packaging Policy

This policy is to provide naming conventions for packages containing shared libraries in /lib{,64} and /usr/lib{,64}. 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.


Package Naming

  • Shared libraries in /lib{,64} or /usr/lib{,64} shall be packaged into rpms whose name is "lib" + $NAME + $NUM.
[$NAME is formed by cutting off the prefix "lib" and suffix ".so.*" from the SONAME]
[If $NAME ends in a digit, a dash is inserted between $NAME and $NUM. So it is libfoo1-0, not libfoo10.]
  • Shared libraries may reside in /lib{,64} or /usr/lib{,64} only if there are development files packages separately in a -devel package to link against those libraries. See (1) on how to handle different cases.
  • Packages named lib$NAME$NUM contain only files named lib*.so.* (no headers, no *.so files, no config files, nothing else). See below (3) for how to make exceptions work.
  • $NUM contains only decimal digits and underscores.
  • $NUM is equal to the shared library SONAME number with dots replaced by underscores. Usually the SONAME number is equal to the SO version MAJOR. For example, if the SONAME is libssl.so.0.9.7 then the SONAME number is 0.9.7, $NUM would be 0_9_7 in this case.
  • 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.

Package Contents

  • lib$NAME$NUM.rpm either contains exactly one shared library named lib$NAME.so.* or it contains multiple shared libraries.
  • lib$NAME$NUM.rpm may only contain multiple shared libraries if the SO versions of all of them change at the same time always, in lockstep.
[If depending on one of the contained libraries doesn't create a dependency on all or most other contained libraries, then it's preferred to not merge those libraries into one rpm, but leave them in their own rpm. Consider installation size. Exceptions are allowed here.]
[Example: libfoo.so.1 is packaged in libfoo1.rpm. Or libbar.so.3, a part of the program suite PLONK 4.1, would be packaged into libplonk41.rpm. The latter only makes sense if that rpm contains more than one shared library, which should be the exception, and can be done only if all shared libs therein will update their SO version in lockstep.]
  • 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,la} and headers.

Best Practices

The following are guidelines for library packaging in general:

- Avoid packaging static libraries. 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.
- Avoid packaging libtool config files (.la files). In general they are not needed if you do not package a static library. If in doubt, ask.
- Shared libraries are not executable.

Exceptions

(1) Shared libraries which are used solely and only by programs from the containing main package must be packaged into the main rpm, without a separate lib$NAME$NUM rpm. The following must be true:

- the shared libs are placed in a subdirectory of /usr/%{_lib}/, that subdirectory is named like the main package
- no devel files for those shared libs are packaged anywhere, no *.a, no *.so, no headers, no files which normally would belong into the lib$NAME-devel package.

(2) A list of packages that are exempt from this policy is: glibc, pam (to be extended with explicit approval only).

(3) If a shared library package lib$NAME$NUM 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.

(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 rpm 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.

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 rpm name, as otherwise the update stack will be confused, and using some monotonically increasing number as that makes sense. A strict structure on rpm names will also help those writing quick&dirty tools.

From that follows that only files should be included therein, which aren't generating file conflicts later if installed together with another libbla* rpm. Hence only lib*.so.* files are allowed in them. To ensure that no shared libraries creep in which aren't handled that way we disallow them to be in any package not named via these rules.

Effectively that creates a partition of all files into shared libs and others, and makes sure that no rpm contains files from both partitions.

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{,64} and /usr/lib{,64}. 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).