More control over warnings for and visibility of deprecated library API via generated export macro header

Or: More preparation for the autumn of version 5 of KDE Frameworks

Consumer and producer interest in legacy in a library API

During the development life-time of a library in a API-compatible major version, quite some part of its API can be found to be insufficient and thus be deprecated in favour of API additions that serve the purpose better. And the producers of the library want to make the consumers of the library aware about those changes, both to make the investment into the improved API pay out for the consumers as well as prepare them for the future removal of the old API. As a deprecation note in the API documentation or release notes might be missed for existing consumer software, the compiler is pulled into the game using deprecation attributes on the API symbols, so developers looking at the build log have a chance to automatically be pointed to use of deprecated API in their software.
Next, once the chance for dropping the legacy parts of an API arrives on a change to a new major version, again having the compiler support pointing out that part is easier then grepping over the codebase for potentially pattern-less informal notes in code comments or the documentation.

At the same time consumers of a library might be well aware about API deprecations. But they might want to support a range of released versions of the library, using a single code base without variants for the different library versions. They might have more important issues to solve than deprecated API and want to get rid of any deprecation warnings at all. Or they want to ensure that no new uses of deprecated API is added.
Others consumers again might want to use custom builds of the library with all the implementation for deprecated API dropped, at least until a certain version, to save size when bundling the library product.

KDE Frameworks: … TODO

KDE Frameworks, the continuation of the “kdelibs” bundle of libraries, but with emphasis on modularization, is now at API-compatible major version 5. Yet one can find legacy API already deprecated in version 3 times, but done so only as comment in the API dox, without support by the compiler. And while lots of API is also properly marked as deprecated to the compiler, the consumer has no KDE Frameworks specific option to control the warnings and visibility. While some “*_NO_DEPRECATED” macros are used, they are not consistently used and usually only for deprecations done at version 5.0.

As you surely are aware, currently the foundations of the next generation of Qt, version 6, are sketched, and with the end of 2020 there even exists a rough date planned for its initial release. Given the API breakage then happening the same can also be expected for the libraries part of KDE Frameworks. And which would be a good time to also get rid of any legacy cruft.

New: ECMGenerateExportHeader, enabling more control about deprecated API

A proposed new addition to KDE’s Extra CMake Modules (ECM) should allow to improve the situation with KDE Frameworks, but also other libraries: ECMGenerateExportHeader (review request).

It would generate an extended export macro definition header which also includes macros to control which parts of the deprecated are warned about, are visible to the compiler for library consumers or included in the build of the library at all. The macros would be inspired by similar macros introduced with Qt 5.13, so the mind model can be transferred.
(Inspired, but not a plain copy, as e.g. the name “QT_DISABLE_DEPRECATED_BEFORE” is a bit misleading, as the macro is specified to work as “before and including”, so the proposed inspired name uses “*_DISABLE_DEPRECATED_BEFORE_AND_AT”.)

More elaborated example usage would be like this (note the difference between FOO_BUILD_DEPRECATED_SINCE and OO_ENABLE_DEPRECATED_SINCE, see documentation for explanation):

CMakeLists.txt:

include(ECMGenerateExportHeader)

set(EXCLUDE_DEPRECATED_BEFORE_AND_AT 0 CACHE STRING "Control what part of deprecated API is excluded from build [default=0].")

ecm_generate_export_header(foo
    VERSION ${FOO_VERSION}
    EXCLUDE_DEPRECATED_BEFORE_AND_AT ${EXCLUDE_DEPRECATED_BEFORE_AND_AT}
    DEPRECATION_VERSIONS 5.0 5.12
)

Installed header foo.hpp:

#include <foo_export.h>

enum Bars {
    One,
#if FOO_BUILD_DEPRECATED_SINCE(5, 0)
    Two,
#endif
    Three,
};

#if FOO_ENABLE_DEPRECATED_SINCE(5, 0)
/**
  * @deprecated Since 5.0
  */
FOO_DEPRECATED_VERSION(5, 0)
void doFoo();
#endif

#if FOO_ENABLE_DEPRECATED_SINCE(5, 12)
/**
  * @deprecated Since 5.12
  */
FOO_DEPRECATED_VERSION(5, 12)
FOO_EXPORT void doBar();
#endif

class Foo {
#if FOO_BUILD_DEPRECATED_SINCE(5, 12)
  /**
    * @deprecated Since 5.12
    */
  FOO_DEPRECATED_VERSION(5, 12)
  virtual void doFoo();
#endif

Source file foo.cpp:

#include "foo.hpp"

#if FOO_BUILD_DEPRECATED_SINCE(5, 0)
void doFoo()
{
    // [...]
}
#endif

#if FOO_BUILD_DEPRECATED_SINCE(5, 12)
void doBar()
{
    // [...]
}
#endif

#if FOO_BUILD_DEPRECATED_SINCE(5, 12)
void Foo::doFoo()
{
    // [...]
}
#endif

Other, better approaches?

The author is not aware of other approaches currently, but would be happy to learn about, to compare, improve, or even discard in favour of another the proposed approach.

Please also have a look at the documentation for the proposed CMake macro ECMGenerateExportHeader (review request) and tell your thoughts.

For this macro being applied, see a patch for KCoreAddons and a patch for KService.