Package and Library Versioning



This is a summary about a recent discussion Stefan and I had about aidacc, libbse and librapicorn versioning, 
comments and corrections are welcome.

Note, an SONAME is the shared-object (library) name, encoded in an ELF library that
determines a library ABI. If the SONAME changes, the ABI changes. Example:
objdump -p librapicorn-15.09.so.1.0.1 | fgrep SONAME
  SONAME               librapicorn-15.09.so.1

# Current versioning State

* Rapicorn encodes the year of a release as MAJOR and the month as MINOR, MICRO is incremented for 
development versions (odd) and releases (even).
* With the recent packaging overhaul (#219d818bce8741abb4befc3520ed823a016c1934), there can be parallel 
installations of rapicorn that have different MAJOR or MINOR versions.
* The SONAME depends on MAJOR.MINOR and MICRO, changing MAJOR or MINOR *forces* ABI breaks; through ABI 
aging, changing MICRO may break or retain ABI (details here: 
https://www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html, we use LT_CURRENT=MICRO).
* Configure supports --enable-devel-mode which enables additional build rules (details below), this currently 
autosenses odd MICRO versions.
* Note that during development ABI may be broken, in such cases the binary age is reset and library linking 
is handled correctly (important for developers and users that try snapshot packages).
* Python modules include MAJOR.MINOR under the hood, e.g. besides "import Rapicorn" this also works: "import 
Rapicorn_15_09 as Rapicorn"
* Rapicorn redefines the C++ namespace to the current MAJOR.MINOR version, e.g. "rapicornconfig.h:#define 
Rapicorn Rapicorn_15_09". This can in theory allow linking different rapicorn library versions into the same 
program.
* The continuous integration scripts that build snapshot packages are using MAJOR.MINOR.MICRO-tCOMMITTOTAL, 
e.g. "15.09.1-t4984", where COMMITTOTAL is monotonically increasing and can be retrieved via `git rev-list 
--count HEAD` in a complete repository, so commits can be related to snapshots.

# Versioning considerations for the future

* It can be desirable to reflect the _year_ of a release in a version number, case: 
http://blog.codinghorror.com/whats-in-a-version-number-anyway/
* We may need to allow parallel installations of different MAJOR versions, in case porting between versions 
takes considerable time. But parallel installations of different MINOR versions is probably overkill.
* For parallel installation, debian packages would be named similar to: 
rapicorn-15_15.09.1-t5011-1_amd64'.deb, i.e. NAME='rapicorn-15', UPSTREAM_VERSION='15.09.1', 
SNAPSHOT_VERSION='t5011' if applicable, DEBIAN_PKGVERSION='1', ARCH='amd64'. See also: 
https://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Version
* To support longer ABI stability periods in the future, we can use LT_CURRENT=MINOR and decouple MINOR from 
the release months. That will allow MINOR version changes to break or _retain_ ABI. But that way MICRO 
version changes will never break ABI.
* Note that ABI breakage during development will then require *MINOR* version bumps. That in turn requires a 
MICRO version reset to zero. So now development versions cannot be easily detected via odd MICROs. That in 
turn means --enable-devel-mode requires different autosensing logic.
* Possible solution: let configure default to --enable-devel-mode=no, but pass --enable-devel-mode=yes 
through autogen.sh (which can be overridden via ./autogen.sh --enable-devel-mode=no), so ./configure in 
tarballs has devel-mode disabled, while normal git repository builds where autogen.sh is used will have it 
enabled.
* Retain Python module versioning for MAJOR version, i.e. support "import Rapicorn" for the latest version 
and: "import Rapicorn_15 as Rapicorn"
* Give up on the C++ namespace versioned renaming, there's no foreseeable use case for linking against 
multiple Rapicorn versions at the same time and some of the dependencies or included code portions like rsvg 
may cause additional problems in such a scenario. C++ library version selection generally happens through 
pkg-config.
* The .pc file becomes: pkg-config rapicorn-15 --print-provides; # rapicorn-15 = 15.09.1
* If lengthy periods of ABI stability are required in the future, we can always switch to keep the MAJOR 
version number stable across multiple years and if it's worth the effort use symbol versioning (glibc does 
this).

# Devel-Mode

This mode is currently enabled for odd MICRO versions, releases currently use even MICRO revisions.
Enabling this mode does the following:
* define -D__FILE_DIR__=\"${abs_srcdir}\" so assert() and friends can produce source locations that are 
easier to debug
* more sources and documentation files are rebuild, which introduces additoinal dependencies: bison, flex, 
doxygen, dot, pandoc, xmllint
* devel-mode requires the .git repository, e.g. for ChangeLog generation
* documentation uploads go to <stable> or 'latest' depending on devel-mode
* additional debugging code can be enabled at runtime by checking Rapicorn::debug_devel_check()

# Related

Just as food for further thoughts on versioning:
- ELF symbol versioning example: https://www.berrange.com/posts/2011/01/13/versioning-in-the-libvirt-library/
- Why to encode the MAJOR version as SONAME and not via `libtool -release`: 
https://autotools.io/libtool/version.html
- Debian New Maintainers' Guide - A.1. Shared libraries: 
https://www.debian.org/doc/manuals/maint-guide/advanced.html#library
- Naming shared library packages (deb): 
https://www.netfort.gr.jp/~dancer/column/libpkg-guide/libpkg-guide.html#naminglibpkg
- C++11 comes with inline namespaces to support symbol versioning at the C++ level: 
http://stackoverflow.com/questions/11016220/what-are-inline-namespaces-for/11018418#11018418


-- 
Yours sincerely,
Tim Janik

https://testbit.eu/timj/
Free software author and speaker.



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]