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
or0.2022.3
- Non native packages get a
+apertisX
revision suffix appended, for instance5.55-3+apertis0
or2.31-13+deb11u2+apertis1
- Downstream append their own suffix as well, for instance the downstream Foo
would have
5.55-3+apertis0+foo2
or2.31-13+deb11u2+apertis1+foo0
- OBS appends a
b${RELEASE}b${BUILDCOUNT}
suffix likebv2022.0b2
orbv2023dev1b1
only to binary packages to ensure each rebuild gets a unique identifier, yielding versions like2.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 Debian9
distribution - Applied to the second revision of Debian’s packaging for the upstream
1.0
release.
- Debian security update
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.
- Binary rebuild of the second revision of Debian’s packaging for the
upstream
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.
- Modification
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.
- Foo downstream modification revision
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 upstream1.0
release. - The
~
ensures that if the installation is upgraded to Debian 10, which uses1.0-7
, the backport is considered less than this and the “newer” package (i.e. the one from Debian 10) gets installed.
- First revision of a backport to Debian
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:
bluez
in Debian has the5.55-3
version- it gets mistakenly uploaded to OBS with no changes, forgetting to add any suffix
- OBS appends the build suffix, generating binaries with version
5.55-3bv2022.0b1
- the
bluez
source gets patched in Apertis and a suffix is appended:apertisX
would yield5.55-3apertis1
while+apertisX
would yield5.55-3+apertis1
- OBS appends the build suffix, generating binaries with version, respectively,
5.55-3apertis1bv2022.0b1
or5.55-3+apertis1b2022.0b1
- the APT published gets the new binaries from OBS:
5.55-3+apertis1b2022.0b1
sorts newer than the already published5.55-3bv2022.0b1
so it correctly replaces it, while5.55-3apertis1bv2022dev2b1
would sort lower than5.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 configure.ac
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.
AC_SUBST([LT_VERSION],[0:0:0])
The following snippet can be used in a Makefile.am
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 instance1706
.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.