Table of Contents:

It is important to properly version software to enable software changes and compatibility of components to be tracked, as well as to aid with bug tracking and the application of updates. To achieve this, it is important that we effectively version each source component, binary package and release.

The approach to versioning depends on the entity being versioned and in the case of source code whether it is developed specifically for Apertis or an existing project used by Apertis.

The overall rules for Apertis are:

  • Apertis-specific packages are handled as native Debian packages, with a simple version without any suffixes, for instance 1.12.3 or 0.2022.3
  • Non native packages get a +apertisX revision suffix appended, for instance 5.55-3+apertis0 or 2.31-13+deb11u2+apertis1
  • Downstream append their own suffix as well, for instance the downstream Foo would have 5.55-3+apertis0+foo2 or 2.31-13+deb11u2+apertis1+foo0
  • OBS appends a b${RELEASE}b${BUILDCOUNT} suffix like bv2022.0b2 or bv2023dev1b1 only to binary packages to ensure each rebuild gets a unique identifier, yielding versions like 2.31-13+deb11u2+apertis1b2022.0b1

Debian Versions and Revisions

The vast majority of the components from which the Apertis distribution is derived are taken from Debian. A large percentage of the packages in Debian are created from source code from other software projects. Each Debian package retains the version set by its upstream (the project from which the source was taken). Debian appends its own package revision to the end of the version. The revision is very important when working with software distributions as this enables tracking of changes across security updates, backports, binary rebuilds, etc.

As an example, imagine an upstream project producing an application called hello releases version 1.0, A Debian packager takes it and makes it’s first release in Debian, following the standards the packager gives the package the version 1.0-1, the first Debian package release. After a while the packager adds some improvements to the packaging, allowing it to cross build, but with the package still based on the upstream 1.0 release. The package is then released as 1.0-2. In the meanwhile, the package is frozen and goes into production, as a so called stable release. Now, the packager continues development and does several Debian updates, creating up to 1.0-7. At this point we’ve got 1.0-2 in a stable distribution and 1.0-7 in a development distribution. The convention is documented the Debian policy. If we find a security issue in stable we want to fix it, but want to keep current version, how would that be named so that version-revision will not conflict with other changes? What about if we want to backport the version in development, to build against stable branch? What about us, as a downstream of the Debian package, how should we version the package if we want to apply some changes on top of Debian package? The convention, when modifying the package for security updates, backports and downstream modification, is to append to the end of the existing Debian version number. As a result of this policy, packages in Apertis bear the addition +apertisX, where X is a incremented number, which shows the number of modifications made to the package by the Apertis team. The +apertis0 suffix means that the only difference between the upstream package from Debian and the package in Apertis is the metadata under debian/apertis/ and the changelog entry itself. This is to highlight the fact that this metadata ends up in the generated source package, so this source package carries a small delta against the corresponding Debian package.

The + has no special treatment in version comparison, but it has been chosen to avoid conflicts with the build suffix appended by OBS.

The symbol ~ is instead special-cased to sort lower than anything else, which can be useful in some very particular cases. For example, 1.0-1+apertis1 is considered greater than 1.0-1, but 1.0-1~apertis1 is lower version than 1.0-1. This ensures that we don’t end up creating packages whose versioning collides with later upstream versioning and allows us to control which package is preferred over another by the package manager which ulitimately uses the version/revision string to decide which package to install.

Further examples:

  • 1.0-2deb9u1:
    • Debian security update 1 for the Debian 9 distribution
    • Applied to the second revision of Debian’s packaging for the upstream 1.0 release.
  • 1.0-2+b1:
    • Binary rebuild of the second revision of Debian’s packaging for the upstream 1.0 release.
    • No changes to the source.
  • 1.0-2+apertis1:
    • Modification 1 by the Apertis team
    • Applied to the second revision of Debian’s packaging for the upstream 1.0 release.
  • 1.0-2ubuntu3+apertis4+foo5:
    • Foo downstream modification revision 5
    • Using the Apertis modification revision 4
    • Based on Ubuntu’s modification revision 3
    • Which was based on the second revision of Debian’s packaging for the upstream 1.0 release.
  • 1.0-7~bpo9+1:
    • First revision of a backport to Debian 9
    • Which is based on revision 7 of Debian’s packaging for the upstream 1.0 release.
    • The ~ ensures that if the installation is upgraded to Debian 10, which uses 1.0-7, the backport is considered less than this and the “newer” package (i.e. the one from Debian 10) gets installed.

If in doubt, we can check on the command line:

$ dpkg --compare-versions 1.0-1+apertis1 gt 1.0-1 && echo YES
$ dpkg --compare-versions 1.0-1~apertis1 lt 1.0-1 && echo YES

Here gt stands for “greater than”, while lt means “less than”.

When making modifications to existing packages, the above rules should be used to determine a suitable version.

Why the +apertisX suffix has been chosen

The +apertisX suffix was chosen over other options like apertisX because the latter may conflict with the build suffix appended by OBS.

For instance:

  1. bluez in Debian has the 5.55-3 version
  2. it gets mistakenly uploaded to OBS with no changes, forgetting to add any suffix
  3. OBS appends the build suffix, generating binaries with version 5.55-3bv2022.0b1
  4. the bluez source gets patched in Apertis and a suffix is appended: apertisX would yield 5.55-3apertis1 while +apertisX would yield 5.55-3+apertis1
  5. OBS appends the build suffix, generating binaries with version, respectively, 5.55-3apertis1bv2022.0b1 or 5.55-3+apertis1b2022.0b1
  6. the APT published gets the new binaries from OBS: 5.55-3+apertis1b2022.0b1 sorts newer than the already published 5.55-3bv2022.0b1 so it correctly replaces it, while 5.55-3apertis1bv2022dev2b1 would sort lower than 5.55-3bv2022.0b1 and it would get erroneously discarded.

Older Apertis releases used the coX suffix only for packages with downstream changes, and started enforcing the +apertisX suffix from the v2022 cycle.

Apertis Source Code Versioning

For packages written specifically for Apertis the package is maintained as a native package. When handling such packages we directly modify the version number rather than appending a revision. Source Code versioning differs for libraries and applications. Applications just require a package version, libraries need a libtool version specified in addition to their package version.

Libtool versioning

Libraries have two version numbers: a libtool version which tracks ABI backwards compatibility, and a package version which tracks feature changes. These are normally incremented in synchronisation, but should be kept separate because ABI backwards compatibility is not necessarily related to feature changes or bug fixes. Furthermore, the two version numbers have different semantics, and cannot be automatically generated from each other.

A good overview of libtool versioning, and the differences from package versioning, is given in the Autotools Mythbuster.

To update the libtool version, follow the algorithm given in the comments below. This is a typical snippet for setting up libtool versioning:

# Before making a release, the LT_VERSION string should be modified. The
# string is of the form c:r:a. Follow these instructions sequentially:
#   1. If the library source code has changed at all since the last update, then
#      increment revision (‘c:r:a’ becomes ‘c:r+1:a’).
#   2. If any interfaces have been added, removed, or changed since the last
#      update, increment current, and set revision to 0.
#   3. If any interfaces have been added since the last public release, then
#      increment age.
#   4. If any interfaces have been removed or changed since the last public
#      release, then set age to 0.

The following snippet can be used in a to pass that version info to libtool:

my_library_la_LDFLAGS = -version-info $(LT_VERSION)

If a package contains more than one library, it should usually have a separate libtool version for each one.

The standard process for making a release of a module increments the libtool version (if the module is a library) and the package version immediately before release. This is called pre-release incrementing.

The use of pre-release increment for libtool versions means that they are only incremented once for all ABI changes in a release. Some projects use post-release increment for package versions to ensure that the package version number is not outdated (i.e. still equal to the previous release) during the development cycle; we achieve this by altering the version number automatically in the build-snapshot script instead.

Package Versioning

The package version number for a library is that passed to AC_INIT(), and the one which is typically known as the project’s version number. For example, the Debian package for a library will use the library’s package version.

Versions of packages developed for Apertis have the form x.y.z and are updated according to the following rules:

  • x starts at 0, and increments when there is a major compatibility break. Libraries with different major versions should usually be parallel-installable.
  • y is the Apertis branch year and month as a single 4-digit number, for instance 1706.
  • z is 0 for the first release to each branch, and goes up for each release (whether it is a feature release or bugfix-only).

Understanding Binary Build Suffixes

For every binary release (Developer, Preview, Product), we add a build suffix string to the packages, which relates to the release name of Apertis. The build suffix gets added to every built .deb package. Having a build suffix helps determine which Apertis release the .deb package was built for.

The suffix string is constructed of: b<Release_Name>b<B_CNT>.

  • The initial b is for backward compatibility ensuring the new suffix string can work well together with older release packages.
  • <Release_Name> refers to the Apertis release’s name.
  • b<B_CNT> refers to being a binary build along with the build count.

The build count is incremented each time the package is built, regardless of whether the source has changed.

For example, for the Apertis Developer Release v2020dev0 we add the string bv2020dev0b1 to the project configuration on OBS when the package is first built. Similarly, for an Apertis Product Release of v2019.0 we add the string bv2019.0b2 to the project configuration on OBS when it is built for the second time with no source change.

Product Point Release Versioning

A product point release rolls the packages previously released in the update and security repositories into the stable branch. It is required to ensure that the packages in the new point release have a higher version than the binary package in the updates or security repositories, it is thus necessary to include the point releases numbering (e.g. v2020.1, v2020.2) in the build suffix.

For example, during the initial release of the v2020 release, the build suffix was bv2020.0b<B_CNT>. The same build suffix is inherited by the updates and security sub-repositories during this time period. Before doing a new point release v2020.1, the build suffix of the main repositories (apertis:v2020:target, apertis:v2020:development, apertis:v2020:sdk, apertis:v2020:hmi etc) is set to vb2020.1b<B_CNT>.

Once this step has been taken, changes previously committed to Apertis update and security branches (such as apertis/v2020-updates and apertis/v2020-security) are merged into the main release branch (apertis/v2020), thus triggering a build and generation of a package with the required point release suffix. This ensures that the packages synced from the updates and security sub-repositories have higher versions than packages from the updates and security sub-repositories. Once the merge has been completed, the equivalent packages in the update and security branches are purged to allow these branches to be used for following updates and security fixes to the new point release.