Table of Contents:
This guide aims to explain how to use the apertis-abi-compare
tool
in order to detect API/ABI breakage between two versions of a library package.
Other Apertis documents are available:
How to use apertis-abi-compare tool
First, install the apertis-dev-tools
package (>= 0.2021.11
) providing the
apertis-abi-compare
tool.
|
|
Then, build you library package using gbp buildpackage
(or dpkg-buildpackage
) for example.
|
|
This has generated several files in the higher level folder:
user@apertis:~/dev/zlib$ cd ..
user@apertis:~/dev$ ls -l
total 1144
-rw-r--r-- 1 user user 96856 Apr 15 09:33 lib32z1-dbgsym_1.2.11.dfsg-2+deb11u1+apertis0_amd64.deb
-rw-r--r-- 1 user user 96620 Apr 15 09:33 lib32z1-dev_1.2.11.dfsg-2+deb11u1+apertis0_amd64.deb
-rw-r--r-- 1 user user 94264 Apr 15 09:33 lib32z1_1.2.11.dfsg-2+deb11u1+apertis0_amd64.deb
drwxr-xr-x 15 user user 4096 Apr 15 09:33 zlib
-rw-r--r-- 1 user user 102404 Apr 15 09:33 zlib1g-dbgsym_1.2.11.dfsg-2+deb11u1+apertis0_amd64.deb
-rw-r--r-- 1 user user 191712 Apr 15 09:33 zlib1g-dev_1.2.11.dfsg-2+deb11u1+apertis0_amd64.deb
-rw-r--r-- 1 user user 50368 Apr 15 09:33 zlib1g-udeb_1.2.11.dfsg-2+deb11u1+apertis0_amd64.udeb
-rw-r--r-- 1 user user 92172 Apr 15 09:33 zlib1g_1.2.11.dfsg-2+deb11u1+apertis0_amd64.deb
-rw-r--r-- 1 user user 23968 Apr 15 09:33 zlib_1.2.11.dfsg-2+deb11u1+apertis0.debian.tar.xz
-rw-r--r-- 1 user user 1903 Apr 15 09:33 zlib_1.2.11.dfsg-2+deb11u1+apertis0.dsc
-rw-r--r-- 1 user user 12293 Apr 15 09:33 zlib_1.2.11.dfsg-2+deb11u1+apertis0_amd64.buildinfo
-rw-r--r-- 1 user user 4127 Apr 15 09:33 zlib_1.2.11.dfsg-2+deb11u1+apertis0_amd64.changes
-rw-r--r-- 1 user user 370248 Apr 14 19:18 zlib_1.2.11.dfsg.orig.tar.gz
Only .deb and .dsc files are used to check API/ABI breakage, so let’s move them to a new folder for clarity:
|
|
Now, we can call apertis-abi-compare
to compare API/ABI between our freshly
created package with the one available in the apt repository. We just need to
give as an argument to apertis-abi-compare
the folder containing our .deb and
.dsc files:
|
|
apertis-abi-compare
reads the .dsc file to determine and download
the previous version of the library available in the apt repository.
Then, it extracts the .deb files of both versions in order to compare
headers and libraries.
apertis-abi-compare
uses abi-compliance-checker
internally to compare
versions. Thus, a library XML descriptor
file can be provided with the library package. If the descriptor file is not
provided, apertis-abi-compare
tries to generate a basic one.
A summary of the analysis is displayed with the number of problems detected
and a comprehensive HTML report has been generated at
compat_reports/z/1:1.2.11.dfsg-2+deb11u1+apertis0bv2023dev2b2_to_1:1.2.11.dfsg-2+deb11u1+apertis0/compat_report.html
.
This report contains a list of problems detected regarding the Binary Compatibility and the Source Compatibility, both in separate tabs.
The first paragraph Test Info contains information about the analyzed library including the library name, compared package versions and architecture.
A second paragraph Test Results reports how many header files, libraries and symbols/types have been analyzed with a percentage of compatibility between both versions. In our example, the compatibility is 100% because we compared the same version of the zlib.
Next, Problem Summary summarizes all issues detected. These issues are sorted by categories: Added Symbols, Removed Symbols, Problems with Data Types, Problems with Symbols and Problems with Constants. Then, by their severity: High, Medium or Low.
Finally, the last parts contain respectively the list of Header files and Libraries analyzed.
Now, let’s simulate an API breakage by downloading a very old zlib
package
(1:1.2.3-11) from snapshot.debian.org
and compare it to the new version (1:1.2.11.dfsg-2). Because the old
version has less functionalities compared to the new one, abi-compliance-checker
will complain about API/ABI breakages.
The package automatically downloaded by apertis-abi-compare
from the apt
repository will always be considered as older than the local one.
|
|
Now, we see the binary and source compatibilities fell to 68%. Several problems and warnings are reported as well.
Opening the report will give use more information about the detected issues:
The Problem Summary informs us that 27 symbols have been removed with a high severity. Two problems have been detected regarding data types or constants, classified in low severity and one medium severity issue with symbols has been detected.
Then, we have the list of Removed Symbols
Followed by the list of detected Problems
:
By clicking on the +
button, the report displays the detected change and its
implications.
The source compatibility tab reports similar information than the binary compatibility tab.
Packages providing multiple libraries
Support of packages providing multiple libraries has been added in
apertis-dev-tools
version 0.2022.1
.
For packages providing multiple libraries, it is highly recommended to provide
a library XML descriptor for each library provided in the package.
abi-compliance-checker
requires this file to analyze a library. In order to
facilitate the analysis, apertis-abi-compare
generates a simple XML descriptor
file that should work in simple case. Unfortunately, this feature is not smart
enough for packages containing multiple libraries. The tool cannot separate
headers between libraries, thus it assigns all headers to each library.
The library descriptor file is handwritten as described in the following library descriptor paragraph.
Finally, the abi-checker
job can be triggered as explained in the
apertis-abi-compare job
paragraph, it doesn’t make any difference whether it is a single library
package or a multiple libraries package.
The zlib
package, used in the above example, is a special case of a source
package providing multiple libraries. From the source package, several binary
packages can be generated zlib1g
, lib64z1
, lib32z1
and libn32z1
.
The main package zlib1g
provides the library in a standard way whereas
the other packages provide a different version of the library. In other words,
they provide a 64 bit version of the library for 32 bits architectures or the
other way around. Thus, all packages provide the same library, but for
different architectures which is not possible to compare with
abi-compliance-checker
.
Therefore, the zlib
package cannot be used by apertis-abi-compare
to test
the support of packages providing multiple libraries.
apertis-abi-compare
will generate a report per analyzed library in the
compat_reports
folder. For instance, if a package provides libmyfirst
and
libmysecond
, then the compat_reports
folder will contain two folders.
The first one, called myfirst
, will contain report for libmyfirst
library
whereas the other folder mysecond
will contain report for libmysecond
library.
Example of library descriptor
To be detected by apertis-abi-compare
, the descriptor file must be named
MYLIB-abi-descriptor.xml
(where MYLIB
is the related library name) and must
be installed by your lib***-dev
package. The recommended installation path is
/usr/share/doc/lib***-dev/
to be in compliance with the Debian Policy, but the
path does not matter because apertis-abi-compare
will scan all files installed
by the lib***-dev
package to find a file called *-abi-descriptor.xml
.
In the source package, the recommendation is to place the descriptor file in
the debian/apertis/abi/
folder in order to avoid mixing it with other
packaging files.
Below is a basic example for zlib
named zlib-abi-descriptor.xml
:
|
|
The library descriptor file is a simple XML file which contain several mandatory and optional sections. Each section is defined by its name and lists library information. A section contains one information per line.
The most important and mandatory sections are version
, headers
and libs
.
The first one describes the version of the analyzed library, the second one
lists all library headers with their installed path and the last one defines
the path to the installed shared library.
In order to prevent having to modify this file for each new version, we use ${VER} and
${PATH} variables which are filled by apertis-abi-compare
. They refer to the
package version and the path where the package was extracted. Although it is
not necessary to use the ${VER} variable, not having the ${PATH} variable will
result in a failure of abi-compliance-checker
.
The list of all optional sections is available on the
library descriptor documentation.
These sections allow, for example, to define additional include paths in case
your library need headers not in the default path to be built (add_include_paths
).
It is also possible to define additional GCC options if required (gcc_options
).
Other possibilities offered by optional sections are to exclude data types
(skip_types
), symbols (skip_symbols
), namespaces (skip_namespaces
),
constants (skip_constants
) and header (skip_headers
) from the analyze.
When changes in the library need to be reflected on the descriptor file, then it is enough just to manually update this file in git for the corresponding new library version.
Please refer to the documentation of abi-compliance-checker
to know more
about the XML library descriptor file:
library descriptor documentation.
How to enable the apertis-abi-compare job in the build pipeline
The apertis-abi-compare
tool is only useful for comparing ABI of different
versions of a library. Thus, it is useless to run the tool on packages not
providing a library, and consequently the associated pipeline job is not enabled
by default. To enable the apertis-abi-compare job in the build pipeline, you
just need to create an empty debian/apertis/abi-compare
in your package.
|
|
Pushing the commit adding the debian/apertis/abi-compare
file will trigger
the pipeline with a new job called abi-checker
.
Let’s use the tests/zlib repository
as an example. The last two commits added a debian/apertis/abi-compare
file
and created a new debian/changelog
entry. Then, go to the
CI/CD > Pipelines
page. As shown in the screenshot below, a new abi-checker
job is available.
By clicking on it, the log is displayed. In case no ABI breaks are detected,
the job will succeed and the report generated by abi-compliance-checker
can
be downloaded by clicking on Download
.
The generated report is also directly displayable by using the Browse
button.
Now, let’s take a look at the tests/zlib_broken
repository. Like in the previous example for apertis-abi-compare
, this
repository contains an old version of zlib in order to simulate an ABI breakage.
The pipeline will try to upload the old version 1.2.3 over the current version
1.2.11, thus some functionalities will be removed and apertis-abi-compare
will detect a breakage. By looking at its
CI/CD > Pipelines page,
we see the abi-checker
job fails.
The job log contains some information regarding the ABI breaks detected, but a comprehensive report can be downloaded.
This report contains all information useful to understand the ABI breaks detected as explained in the previous part above.
Limitations
-
The
abi-compliance-checker
tool which is used byapertis-abi-compare
is only able to compare libraries for the current architecture. Indeed, it compiles headers in order to create an ABI dump. Since GitLab runners run on Intel machines, this means only x86_64 can be checked and we can’t check the ABI of ARM packages. -
To analyze packages containing multiple libraries it is highly recommended to provide XML descriptors. See the Packages providing multiple libraries paragraph for a comprehensive explanation.
FAQ
abi-compliance-checker fails to build headers
abi-compliance-checker
can fails to build headers of your library. In this
case, you need to provide and customize a library XML descriptor. Please refer
to the documentation of abi-compliance-checker
to know how to write it:
library descriptor documentation.
Troubleshooting
In some cases, abi-compliance-checker
fails to compile headers and displays
the following error messages:
|
|
The given log will provide useful information regarding what was wrong.
The first step is to check if there is no missing dependency. The library
development package (libXXX-dev
) should contain all dependencies required
to compile it. If missing dependencies are found, they should be installed and
added to the Depends
field of the -dev
package.
If all dependencies are defined and installed, then it is most likely an issue in the header file. The most common reason is this header does not compile on its own. Compilation of this header can be simply tested with:
|
|
For example, trying to compile a header gives this error message:
error: ‘queue’ in namespace ‘std’ does not name a template type
note: ‘std::queue’ is defined in header ‘<queue>’; did you forget to ‘#include <queue>’?
In this example, the std::queue
class is used in the header without including
#include <queue>
in the header makes it compilable. This was
an easy case because the solution was given as a note in the log, but the
solution is not always given and could require some investigations.
The failing header was not self-contained. In other words, it refers to a symbol defined in another header file without directly including this header file. The good practice is to always include headers defining or declaring symbols used (See Google C++ Style Guide#Include What You Use, Google C++ Style Guide#Self contained Headers or C++ Core Guidelines#Header files should be self-contained).
For more general good practices regarding header files, see Google C++ Style Guide#Header Files.