Regarding the "annotation processors" on compile classpath warning in Gradle 4.6


(mauromol) #1

Hello,
I’ve upgraded my build script to use Gradle 4.6.
The following warning is now printed:

Deprecated Gradle features were used in this build, making it incompatible with Gradle 5.0.

Running with --warning-mode=all, I get:

Putting annotation processors on the compile classpath has been deprecated and is scheduled to be removed in Gradle 5.0. Please add them to the processor path instead. If these processors were unintentionally leaked on the compile classpath, use the -proc:none compiler option to ignore them..

However, I don’t know what annotation processors are and I don’t use them. I suspect there’s some annotation processor in any of the JARs I depend on. So, multiple questions come into my mind:

  • which annotation processors were found and in which JARs? I ran Gradle even with --debug but I see no further information on where these processors were found
  • how can I control this, if the JARs I depend on include annotation processors? I don’t have any control on them
  • why is this signalled as a problem with the build? If I depend on a JAR that includes an annotation processor, it’s not a build problem, unless the annotation processor is introduced by a Gradle plugin I’m using, but once again I can’t find any clue on which plugin may be the source of this
  • what do you mean by “removed in Gradle 5.0”? My project won’t compile any more?

Honestly I’m a bit confused.

Thanks in advance for any help.
Mauro


(Stefan Oehme) #2

Hey Mauro,

putting them on the compile classpath deactivates compile avoidance, slowing down your builds. The warning describes what you can do if you didn’t intend to use annotation processors (use -proc:none). The list of processors is something I’ll add for 4.7. Gradle 5.0 will ignore processors on the compile classpath. If you didn’t use them, everything is fine. If you did use them, your project would stop to compile at that point.

Cheers,
Stefan


(mauromol) #3

Hi Stefan,
thanks for your clarifications. I did know that putting them on the compile classpath may slow down builds… but, do they slow down builds “automatically” or only if I enable/use them in some way? I think only in the second case, am I correct?
So, isn’t Gradle able to detect whether they are actually used or not, and then emit the warning only in the first case?
Unfortunately, I don’t know how they work, so I don’t know if that’s feasible or not.

In any case, the addition to Gradle 4.7 of a list of the detected annotation processors is very welcome.

Cheers,
Mauro


(Stefan Oehme) #4

No, they always slow your build down. The optimizations we do come before compilation, but we only know whether processors were used after compilation.


(Steve Cohen) #5

Please explain what “(use -proc:none)” means. Where does one put this?

I must say that I am not thrilled with this change. In our environment compilation speed is NOT an important issue, and breaking builds because of this IMHO ought to have been made an opt-in feature.

OK, found -proc:none on the oracle java site.

-proc: {none,only}
Controls whether annotation processing and/or compilation is done. -proc:none means that compilation takes place without annotation processing. -proc:only means that only annotation processing is done, without any subsequent compilation.

So if I understand correctly, to upgrade to Gradle 5.0 when it comes out, we will have to comb over our source code and gradle scripts and add -proc:none to all scripts (we have hundreds) where we aren’t using annotations and then do what, exactly, in cases where we have code that IS using annotations?


(James Justinic) #6

You shouldn’t have a breaking change unless you require annotation processors (not just annotations) AND have been doing so incorrectly (or at least ignoring the recommended way of doing so for over a year).

If you don’t use annotation processors, you don’t have to change anything. You won’t be negatively impacted, BUT if one of your dependencies unintentionally leaked a processor into your compile classpath that you didn’t want/need, you will get a free performance boost in Gradle 5.0. Setting options.compilerArgs += '-proc:none' on your compile tasks now will have a similar effect without waiting, and will remove the warning.

If you do require annotation processors, and set options.annotationProcessorPath on your compile tasks with the required processors, you also don’t need to do anything. However, you can reduce configuration code by declaring the processor on the newly added annotationProcessor configuration instead of creating / configuring manually if you want.

The breaking case is if you require the code generated by the annotation processor, but don’t do anything other than put your annotation processor on the compile classpath. You shouldn’t have to comb over code. If you build with Gradle 5.0 when it comes out, the generated code will be missing, and anything that tries to use it will break. Your solution is to add the annotation processor dependency to the annotationProcessor configuration. You can add options.compilerArgs += '-proc:none' to preview the breakage, if you’re so inclined.


(Steve Cohen) #7

Thanks, . I don’t even know what an annotation processor is. But evidently something in our build is using one. How does one even tell? What does one look like? And what is “leaking a processor into your compile classpath”? In the build that’s getting the warning, the only dependencies on the compile classpath are log4j 2.5 and a couple of ancient home-grown jars that don’t use annotations let alone annotation processors. Do you know if log4j2 in version 2.5 exhibited this problem?

In general, I am opposed to having to do anything to AVOID an unneeded optimization.


(James Justinic) #8

An annotation processor can be used to generate or modify your source based on annotations. Using one is normally a pretty intentional decision. You’re trying to have something done to your code at compile time other than just compile it. You should expect to have to do something in your build to make this happen. Declaring the dependency in the correct location is pretty minimal requirement.

For example, take Project Lombok, that would allow you to write the following code, and their annotation processor will generate the constructor / getters for you. Without the annotation processor, this code wouldn’t compile:

@Data
public class Pair {
    private final String key;
    private final String value;
} 

Yes, log4j contains an annotation processor. It’s bundled in the core log4j JAR file, but doesn’t actually do anything unless you are writing log4j plugins. You would have code that is implemented according to their plugin API and annotated with a specific log4j plugin annotations. If your project isn’t doing that, there’s absolutely no reason for the annotation processor to run. It’s just causing wasted resources.


(Steve Cohen) #9

OK, I’m starting to calm down :slight_smile:. So as I understand it, the “culprit” here is the annotation processor in log4j and/or putting this on the compile classpath. Nothing will happen to my code unless my code includes log4j plugins which it doesn’t. When 5.0 comes out, if we use it, we will get a “free” speed improvement (which we don’t particularly need, but let’s not look a gift horse in the mouth).

Have I got that right?

The only question I have left is how this processor is getting “leaked into the classpath”. Could log4j2 have been coded differently so that this would never happen? And is there a version where they’ve fixed this? Or is the problem my use of log4j in my app’s build script?

def log4jVer = '2.5'

dependencies {
	compile("org.apache.logging.log4j:log4j-api:${log4jVer}")
	compile("org.apache.logging.log4j:log4j-core:${log4jVer}")
	compile("org.apache.logging.log4j:log4j-web:${log4jVer}")
	compile("org.apache.logging.log4j:log4j-jcl:${log4jVer}")

I am building a war file in which the log4j jars will be added to WEB-INF/libs. Code depends on log4j but not its annotation processor? Is there a better way I could be doing that? I don’t see one. So without that, I have to live with the warning (unless I use the -proc:none option), secure in the knowledge that with 5.0, nothing bad will happen.

I still feel that those who want this optimization ought to be willing to ask for it explicitly.


(James Justinic) #10

Yes, I think you’ve got it.

There’s not really a better way you could have done this with what you have. Log4j bundles their annotation processor in the core JAR. They could have separated it out like the api, core, web, and jcl JARs have been. You wouldn’t have depended on it.


(Francisco Moura) #11

Hi!
I faced with the same problem reported by @mauromol, specifically with IntelliJ and lombok.
The options.compilerArgs += '-proc:none' suggested by @jjustinic does not work for me.
After reading Gradle v2.12 and up, Convenient declaration of annotation processor dependencies, Explicitly declaring the annotation processor classpath, net.ltgt.apt and Example usage I end up with the following solution in the build.gradle:

 // lombok configuration
 plugins {
    id 'net.ltgt.apt' version '0.15'
 }

dependencies {
    // lombok
    compileOnly('org.projectlombok:lombok:1.16.20')
    annotationProcessor 'org.projectlombok:lombok:1.16.20'
}
  • Important: gradle-apt-plugin version as described in the Lombok project documentation does not work for me. So the latest version of gradle-apt-plugin was applied.

Thanks for your appreciation!


(James Justinic) #12

If you’re using lombok, you’re using annotation processors. This topic was primarily a discussion about what users who are NOT using annotation processors can do about the annotation processor warning. You can’t only follow the instructions for not using annotation processors if you are using annotation processors.


(Justin Rogers) #13

I am in a situation where I use both an annotation processor, Lombok, defined correctly in my build.gradle file, and I have a dependency that leaks an annotation processor to the compile classpath, Vavr. The solution is a little tedious: exclude the transitive Vavr dependency and re-add the transitive dependency “correctly”:

annotationProcessor 'org.projectlombok:lombok:1.16.20'
compileOnly 'org.projectlombok:lombok:1.16.20'
    
compile('io.vavr:vavr:0.9.2') {
    exclude group: 'io.vavr', module: 'vavr-match'
}
annotationProcessor 'io.vavr:vavr-match:0.9.2'
compileOnly 'io.vavr:vavr-match:0.9.2'

This really seems less than ideal: what if my project was more complicated and I didn’t know where the offending transitive dependency was? Gradle’s warning message does not provide any information about the offending dependency.

If I needed to use annotation processors (and correctly add them to build.gradle) but have a deep transitive dependency that puts its annotation processor on the compile path, won’t my build break when I upgrade to Gradle 5? (My case is a little special because I know I’m using Vavr’s annotation processor, so I knew it was the misbehaving library, but I think the question still holds.)


(Justin Rogers) #14

I suppose I aught to correct myself: it turns out Gradle 4.7 (unlike Gradle 4.6) adds the offending annotation processor to the message:

The following annotation processors were detected on the compile classpath: 'io.vavr.match.PatternsProcessor'. Detecting annotation processors on the compile classpath is deprecated and Gradle 5.0 will ignore them. Please add them to the annotation processor path instead. If you did not intend to use annotation processors, you can use the '-proc:none' compiler argument to ignore them.

Which was a great addition.


(James Justinic) #15

No, if you’ve correctly added the annotation processors required to the annotationProcessor configuration, your build will not break. The mandatory change is that for an annotation processor to execute, it must be added to the annotationProcessor configuration. It’s not required that you exclude the annotation processor from anywhere, but the processor will not execute just because it’s there (as it does now).

The case you’d have a problem is if you’re using vavr-match, but don’t really know what’s going on. This basically would mean you’re annotating classes with @Patterns and @Unapply, but are blissfully ignorant of how the *Patterns classes that you didn’t write showed up in your package. Hopefully that is not the case for the person making changes to the build.

Omitting the noise around things you can optionally do to preview the Gradle 5.0 behavior or do additional cleanup, the actual impact of the change is pretty simple:


(nerro) #16

@jjustinic, small off-topic. In which program is the diagram drawn? Looks very good.


(James Justinic) #17

It was drawn in Creately: Diagram Maker. That diagram is the only time I’ve used the tool, so I can’t say much else about it.


(Alex Hatzenbuhler) #18

I’m having a similar issue here. I’m currently using Gradle 4.8 and IntelliJ 2018.1.4. I have a @ConfigurationProperties class and in my build.gradle I have:

annotationProcessor "org.springframework.boot:spring-boot-configuration-processor:${springBootVersion}"

Right now IntelliJ is giving me the following warning:

Spring Boot Configuration Annotation not found in classpath

It seems that the right thing to do from a Gradle standpoint is not have the annotation processor on the classpath (which is why I’m using annotationProcessor instead of compileOnly. Is this an issue with Intellij? I’m currently not able to use this processor properly, so I’m unsure if it’s an IntelliJ issue or a dependency issue.


(Glen Miller) #19

We are using Lombok and Hibernate, when we add the following to fix the classpath warnings, it works fine under Java 8, but does not compile under Java 10:

annotationProcessor group: ‘org.projectlombok’, name: ‘lombok’, version: ‘1.18.2’
compileOnly group: ‘org.projectlombok’, name: ‘lombok’, version: ‘1.18.2’
annotationProcessor group: ‘org.hibernate’, name: ‘hibernate-jpamodelgen’, version: ‘5.1.0.Final’
compileOnly group: ‘org.hibernate’, name: ‘hibernate-jpamodelgen’, version: ‘5.1.0.Final’

The error is

java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException

even though we are including the jaxws-rt dependency in our project.

Is there something else needed for Java 9/10? This worked fine under Java 10 before making this change.


(Jörn Huxhorn) #20

Is adding

compileJava {
    options.compilerArgs += '-proc:none'
}

supposed to get rid of this Gradle deprecation warning?

4.10.2 keeps warning me even after the proposed fix has been put into place.