!!! DRAFT !!! THIS VERSION IS A DRAFT FOR DISCUSSION AMONG THE SIGNATARIES, DO NOT SEND! THE CASE FOR PARTIAL SYMBOL VERSIONING SUPPORT IN KDE FRAMEWORKS 5 ================================================================== Proposal: --------- We would like KDE Frameworks 5 to start using symbol versioning in the following simple way, which would help a lot with RPM packaging: * Consider a library whose export macro is named "FOO_EXPORT". (Note: We refer to the export macros because those names are already canonicalized. The proposal otherwise does not depend on them.) * Consider an exported function "bar" introduced in version 1.2 of the library. * Then, on platforms that support symbol versioning, we propose to version that symbol "bar" with "FOO_1.2". For the others, nothing should change. Non-goals: ---------- What we explicitly are NOT asking for is supporting multiple versions of the same symbol. In other words, the above function "bar" will always be known as "bar@FOO_1.2" (on platforms which support symbol versioning, and just "bar" on the others) and maintain binary compatibility as before. There shall NOT be another version (e.g. "bar@FOO_1.3") of the same symbol. This allows introducing symbol versioning without degrading support for those platforms which do not support it. This is what we mean by the word "partial" in the title. Rationale: ---------- Of course, you may wonder what the objective of such a strange proposal is. One might think that the restriction given in the "non-goals" section makes symbol versioning entirely useless. That is, however, not the case: The mere presence of the versioning helps RPM, and thus us packagers, a lot. Let us explain what RPM does with those symbol versions: * Consider a library libfoo.so that maintains backwards, but not forwards, compatibility. The soname thus stays constant at, say, libfoo.so.6. This was the situation for all the libraries in kdelibs 4 and is expected to be the case for most, if not all, the libraries in KDE Frameworks 5. * Now consider a function "bar" added in libfoo 1.2, a backwards-compatible feature release. * Consider an application "myapp" linked to libfoo that calls the function "bar". A. Now consider the case where libfoo does not use symbol versioning. A.1. RPM will detect that libfoo Provides libfoo.so.6. A.2. RPM will also detect that myapp Requires libfoo.so.6. A.3. RPM will however, NOT, remember the specific function "bar" that myapp uses from libfoo. This restriction exists for purely practical reasons: Libraries tend to have many symbols. Keeping track of every single such symbol would, unfortunately, blow the metadata up to astronomical proportions. A.4. As a result, RPM will NOT prevent installing myapp with an old libfoo 1.1 that does not export the required function "bar", and myapp will not run. People will blame our packaging for that. A.5. We used to work around this issue for kdelibs 4 with the following approach: * We defined an RPM macro called %{_kde4_version} which expands to the version of kdelibs found at build time: %_kde4_version %((kde4-config --kde-version 2>/dev/null || echo 4.3.98) | cut -d' ' -f1 ) * We then manually added versioned runtime Requires next to the BuildRequires to enforce the versioning, e.g.: Requires: kde-runtime%{?_kde4_version: >= %{_kde4_version}} A.6. Unfortunately, because the Frameworks will by their nature have separate version numbers, the above approach will not scale anymore. Even in KDE 4 land, there were probably places where such a versioned Requires should have been used and we forgot it. But in Frameworks, not only would we need a separate *_version macro for every library, but there would also be many more Requires needed. Therefore, we expect the above manual approach to not scale in the Frameworks world. B. Now consider instead the case where libfoo DOES use symbol versioning. B.1. RPM will detect that libfoo Provides libfoo.so.6 and libfoo.so.6(FOO_1.2) (and probably also libfoo.so.6(FOO_1.0) and libfoo.so.6(FOO_1.1), but that does not matter in this simple example). As you can see, while RPM does not collect the full list of symbols, it does collect the symbol VERSIONS that were used in the library. But it does even more than that: B.2. RPM will also detect that myapp Requires libfoo.so.6 and libfoo.so.6(FOO_1.2). For every symbol that myapp uses from libfoo, RPM remembers its symbol version (if it had one) and puts it in the Requires (of course only once per symbol version). B.3. As a result, RPM will know that myapp requires at least libfoo 1.2 (well, technically, any libfoo that Provides: libfoo.so.6(FOO_1.2), but that is equivalent to libfoo >= 1.2 (and < the next backwards-incompatible release) in practice) and so will any dependency solvers (such as yum). B.4. The result will be much less work for packagers and happy users, at little to no cost to KDE (see below). Proposed Implementation: ------------------------ KDE already keeps track of the version any particular symbol was introduced in for documentation purposes, using the @since Doxygen annotation. We propose to write a tool or script, or to modify Doxygen, to automatically generate a linker version script from those @since annotations. The version script would have to comply to the following format: http://sourceware.org/binutils/docs-2.23.1/ld/VERSION.html On targets which support this linker version script format (e.g., ELF with GNU ld), the script would be passed to the linker when linking the library. On any other targets, nothing would change. KDE Frameworks 5 provides us an opportunity to introduce this feature, and at the same time, the new versioning system makes it all the more important. Please let us take advantage of this opportunity to make packagers and users happy. Signed, Kevin Kofler (Fedora) Rex Dieter (Fedora) (your name here)