Re: Package and Library Versioning



Related to this, here is input from Debian packaging:

* Every time the shared library ABI changes in a way that may break binaries linked against older versions of 
the shared library, the SONAME of the library and the corresponding name for the binary package containing 
the runtime shared library should change. - 
https://www.debian.org/doc/debian-policy/ch-sharedlibs.html#s-sharedlibs-runtime

* If there are development files associated with a shared library, the source package needs to generate a 
binary development package named libraryname-dev, or if you need to support multiple development versions at 
a time, librarynameAPIVERSION-dev. - https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=568374

* Note that using an unversioned package name like libraryname-dev often allows simpler transitions, because 
depending packages that just need recompilation against the new APIVERSION can be upgraded via binNMU 
(non-maintainer binary upload), e.g. by the release team.

I.e. runtime libraries and their package name need to undergo version changes as frequent as the ABI changes. 
But *-dev packages (and development tools/files like .pc, sfidl, aidacc, rapidres) only need to be versioned 
if transitioning takes significant efforts and longer periods are expected where teams are developing against 
2 different APIs that are both actively maintained.

For Rapicorn and libbse that means -dev packages and the development tools can be built and packaged 
unversioned for now.
If it ever becomes neccessary, extra versions can still be added, similar to how Gtk+ had: libgtk-dev, 
libgtk2.0-dev, libgtk-3-dev.


On 18.10.2015 21:42, Tim Janik wrote:
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



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