Hi Gary,

On 9.07.2025 14:35, Gary Gregory wrote:
> Dare I ask my our build has Gradle horror in it?

It’s not a horror—it’s a tragedy. A well-intentioned series of events
that slowly spiraled into pain:



## Episode 1: Runtime-Invisible Annotations

Over the past two years, we began using several compile-time annotations
with retention policy `CLASS`:

* `@BaselineIgnore`: Suppresses binary compatibility warnings for
changes that are technically incompatible but safe in practice—for
example, removing a `public` class clearly marked as internal.

* `@SuppressFBWarnings`: Suppresses SpotBugs reports for code that may
appear insecure (e.g. file handling) but is verified to be safe.

* `@InlineMe`: An Error Prone annotation that helps users migrate away
from deprecated methods by suggesting inline replacements.

These annotations are not retained at runtime, so we declared the
corresponding libraries with `provided` scope, ensuring they aren't
transitively exposed to downstream users.



## Episode 2: The JDK Linter Bug

Despite being non-runtime annotations, the JDK’s `-Xlint:classfile`
option still expects these annotations to be present at compile time. If
they're missing, the linter emits warnings.

This behavior seemed questionable, so I filed a bug report with the JDK
team:

https://bugs.openjdk.org/browse/JDK-8342833

While opinions vary on whether this check is useful, it introduced
real-world complications in builds that rely on clean linter output.



## Episode 3: Build Failures and User Complaints

The combination of annotations and linter sensitivity caused build
failures for users compiling with strict flags like
`-Xlint:all -Werror`.

Example issue report and analysis:

https://github.com/apache/logging-log4j2/issues/3110#issuecomment-2423586754

Although most of these annotations are in `log4j-core`—which shouldn't
typically be on users' compile classpaths—many projects still include it
during compilation, intentionally or not.



## Episode 4: Enter GMM

Adding runtime dependencies just to silence compile-time linter warnings
was not acceptable, but Maven’s limited scope model offers no good
workaround. As a result, Maven users were simply told to ignore the
warnings.

However, for Gradle users, a better option emerged: Jendrik proposed
using the Gradle Module Metadata Maven Plugin in

https://github.com/apache/logging-log4j2/issues/3437

This plugin lets us publish enriched metadata that tells Gradle which
dependencies are compile-only but transitive. Gradle can then resolve
annotation libraries during compilation—without leaking them into the
runtime classpath.



## Finale: Version 2.25.0 – Return of the Karma

The plot thickened in the `2.25.0` release, when we unintentionally
upgraded `spotbugs-annotations` to version `4.9.0`—a version **not
compatible with Java 8**.

At first glance, this seemed harmless. After all, `spotbugs-annotations`
is a **compile-only** dependency. If you’re compiling with JDK 11+ and
targeting Java 8, everything should be fine... right?

Unfortunately, no.

Due to a perfect storm of factors—including `spotbugs-annotations`
publishing its own Gradle Module Metadata and our own use of the GMM
plugin—Gradle users compiling for Java 8 found themselves unable to
build against Log4j `2.25.0`.

The issue is documented here:

https://github.com/apache/logging-log4j2/issues/3754

So despite our best intentions, trying to improve metadata fidelity
ended up triggering build failures for a portion of our users. The karma
of well-meant tooling struck back.

Scene,
Piotr


Reply via email to