Working with symbols files
Symbols files provide a way of tracking the exported symbols of a library, which helps in fine-tuning the library dependencies generated by dpkg-shlibdeps and tracking (partially) binary compatibility. You can find more information about the nature of symbols files here.
This page explains how to use symbols files, that in packages maintained by the KDE team. It requires that you use pkg-kde-tools, which provides all the mentioned pkgkde-* utilities.
Not only KDE packages can benefit from handling symbolsfiles with pkgkde-symbolshelper. If the package is written in C++ and has arch dependend symbols pkgkde-symbolshelper will help you handling those.
Using symbolshelper in your package
To use symbolshelper for your package, you simply have to add pkg-kde-tools to your Build-Depends.
If you use dh you can simply add to your rules file:
%: dh $@ --with pkgkde_symbolshelper
Creating a symbols file
Let's suppose that you have a library package that is called libfoo1 and its version is 1.7. First you need to create symbols file using pkgkde-gensymbols and then convert it to the format used by pkgkde-symbolshelper. The following commands demonstrate how to do this.
pkgkde-gensymbols -plibfoo1 -v1.7 -Osymbols.amd64 -edebian/libfoo1/usr/lib/libfoo.so.1 pkgkde-symbolshelper create -o debian/libfoo1.symbols -v 1.7 symbols.amd64
The above commands should be invoked from the top level directory of your package's source after having build the package and before cleaning (so that debian/libfoo1/usr/lib/libfoo.so.1 exists). "symbols.amd64" is the filename of the intermediate symbols file that will be generated by pkgkde-gensymbols. This filename must be in the format "name.architecture" or "name_architecture" where "architecture" is the cpu architecture on which you have built this library package.
Updating a symbols file for a new library version
After building a new version of the library, symbols may have been added or removed, and in both cases you need to check and update the symbols file. During the build, a diff between the current symbols file and the one that should be there instead will be printed in the output. So, as a first step, you need to save the build log in a file. You can do that by piping the output of your build command through tee, for example:
dpkg-buildpackage -j4 | tee buildlog
In case you forgot to pipe the output and you want to avoid rebuilding, you can also copy-paste the part of the output that contains the diff in new file.
After saving the diff in a file, you can patch the symbols file by executing:
pkgkde-symbolshelper patch -p libfoo1 -v 1.8 < buildlog
Here we suppose that your libfoo1 package was updated to version 1.8, so "-v 1.8" in the command is the switch to specify the new version.
Updating multiple symbols files at once
In source packages that provide multiple binary library packages (for example, kde4libs), it is useful to have a method to patch all the symbol files at once, instead of patching each one separately. pkgkde-symbolshelper offers a way to do this using the following command:
pkgkde-symbolshelper batchpatch -v 1.8 buildlog
where 1.8 is the new version of the source package and buildlog is the log file that contains the appropriate diffs, as in the above section.
You can get the symbols for different archs including those from www.ports.debian.org all at once by running inside the top-level source directory the getbuildlog script as provided by the devscripts package:
getbuildlog <source> <version>
For example let's suppose you want to get the build logs for qtbase-opensource-src's last upload:
getbuildlog qtbase-opensource-src last
It is much easier to use pkgkde-getbuildlogs to get all buildlogs from buildd.debian.org:
pkgkde-getbuildlogs <Package> <Suite>
For example, if qtbase-opensource-src compilation failed because symbol files need to be updated:
pkgkde-getbuildlogs qtbase-opensource-src sid
After that you will find the buildlogs for each arch in the same directory. To patch the symbols files at once, run:
pkgkde-symbolshelper batchpatch -v 1.8 foo_1.8-1*.build
Handling missing symbols
In certain cases, some symbols may be marked as MISSING after updating the symbols file of a library to a new version. This means that a function or class or global variable that was previously exported for use by applications is not exported anymore. In general, removing a symbol from a library causes binary incompatibility and in this case the SONAME of the library should be bumped. However, there are some cases where it is safe to remove a symbol. To ensure that your library stays binary compatible, you should manually check every missing symbol that you see to see if it is safe to be removed or not. The most common cases where it is safe to remove a symbol are:
- The symbol is a function/class/variable that is only used internally in the library and does not exist in any public header.
- The symbol is a private member of a class.
- The symbol is a virtual method that is already provided by the parent class.
If your library is C++, you will probably need a way to translate symbols to human-readable C++ identifiers. To do this, you can use the c++filt command, passing it as a first argument the symbol, striping anything after the first @ character (usually symbols end with @Base). For example, if the symbol is "_ZN10KAboutData10setVersionERK10QByteArray@Base", you can use it like this:
$ c++filt _ZN10KAboutData10setVersionERK10QByteArray KAboutData::setVersion(QByteArray const&)
After verifying that a symbol is safe to be removed, you can just remove the respective MISSING line from the symbols file. If at least one symbol is not safe to be removed, then you should cleanup all the MISSING lines from the symbols file and bump the SONAME of the library.
Notes on binary compatibility checking
Symbols checking provides a nice way of checking if a library stays binary compatible among releases. However, you should note that symbols checking alone is not sufficient to ensure that a library stays binary compatible. There can be other kinds of binary incompatible changes, such as changing the sizes of classes/structs, adding/removing virtual methods, etc, which cannot be catched by checking the symbols.