Java annotation processing (build and Eclipse)

1,031 views
Skip to first unread message

Punyashloka Biswal

unread,
Apr 6, 2015, 12:30:45 PM4/6/15
to gradl...@googlegroups.com

This page describes how to make Gradle's eclipse task populate the annotation processing factory path in Eclipse, so that IDE users can take advantage of annotation processing without having to switch context to Gradle. Are there any plans to make annotation processing a more first-class part of the Java project model in Gradle, and extend IDE support? Concretely, it would be cool if I could simply say

dependencies {
  annotationProcessor 'com.google.auto.value:auto-value:1.0'
}

... and have ./gradlew build./gradlew eclipse and ./gradlew idea work without further configuration.

I'd be happy to help with this if someone familiar with the code can provide a little guidance.

Luke Daley

unread,
Apr 6, 2015, 6:35:47 PM4/6/15
to Punyashloka Biswal, gradl...@googlegroups.com
There’s a couple of interim steps here:

1. Add a `processorpath` property to CompileOptions (like the existing ‘sourcepath’)
2. Add a `processorpath` property to SourceSet (like existing ‘compileClasspath’)
3. Update the Java plugin to wire #2 to #1
4. Create default/conventional dependency configurations that are wired to #3
5. (more stuff I haven’t thought of)

We might want to do some strong modelling of the other annotation processor relevant options too. 
Having the IDEs support this is really dependent on #2. Getting to something like your example means #4.
That’s all reasonably straightforward stuff, if we could agree on #2 (i.e. the exact API changes).

If you’re interested in progressing this, the best thing to do would be to submit what we call a “design spec” with a proposal for these changes.


Starting something is more important than getting it right before submitting. We can iterate on the pull request.
--
You received this message because you are subscribed to the Google Groups "gradle-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to gradle-dev+...@googlegroups.com.
To post to this group, send email to gradl...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/gradle-dev/8e2e3daa-b0d3-4825-be78-bba19f023c0c%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Punyashloka Biswal

unread,
Apr 7, 2015, 12:04:12 PM4/7/15
to gradl...@googlegroups.com, punya....@gmail.com
Hi Luke,

I wrote up a bare-bones design spec at https://github.com/gradle/gradle/pull/431 and would appreciate any feedback.

Regards,
Punya

Thomas Broyer

unread,
Apr 26, 2015, 7:24:46 AM4/26/15
to gradl...@googlegroups.com, punya....@gmail.com
Hi,

I too am interested in this topic, and am starting setting up a plugin to stop repeating myself in each project.

One thing that I needed too is a "compileOnly" configuration for annotations that are needed at compile-time to trigger processors but not needed at runtime.
For example, Immutables.org 2.x or AutoValue (or AutoFactory) have a single JAR containing both annotations and the annotation processor, so you'll want to add the dependency to both the compileClasspath (but not runtimeClasspath) and the processorpath.
I currently define 2 configurations (4 if you count the "test" ones): compileOnly and apt. compileOnly is added to the compileJava task's classpath, and apt is passed as a -processorpath in compilerArgs.
For now, my compileOnly doesn't extend compile, it's added to the classpath instead (if it extended the compile configuration, I could replace the classpath to the compileOnly configuration), and apt doesn't extend compileOnly (when a processor comes in 2 JARs, such as Dagger, it'll likely end up in the processorpath anyway as a transitive dependency; and I specifically don't want the processorpath to be a superset of the classpath).

Other things to consider: annotation arguments (configured as a map) and list of processors to run.

The default behavior when the processorpath is empty could be defined using a flag: don't pass -processorpath so annotation processors are looked up in the classpath (backwards-compatible), or pass an empty -processorpath.
For now, my plugin always passes a -processorpath (if you apply the plugin, then you want to manage annotation processors using the new apt configuration, so it makes sense; "applying the plugin" would become the new flag to the JavaCompile task)

Thomas Broyer

unread,
May 7, 2015, 8:18:27 AM5/7/15
to gradl...@googlegroups.com, punya....@gmail.com
So, it took me a bit of time (I had to learn nebula-test, Spock, and a bit more of Groovy, and I tried many many options) but I finally have something I'm happy with as a first step: https://github.com/tbroyer/gradle-apt-plugin/
(I cleaned up my history so you won't see the myriad of things I tried out)
Compared to what I was doing in my build scripts, I changed the compileOnly configurations to extend from 'compile' and assigned them as the sourceSets' compileClasspath (instead of only configuring the JavaCompile tasks' classpath).

I think I could easily add a processorpath, processors and processorArgs properties to JavaCompile's CompileOptions (similar to how I already added sourcepath), defaulting to an empty processorpath (possible breaking change, just assign processorpath=classpath to revert to previous/current behavior, just like we did with sourcepath).
If processors is the empty list, then no -processor argument is passed to javac and the default discovery process is used.
processorArgs is a Map<String,?> and each entry is passed as -A<key>=<value>

I could probably also add the new configurations, setting sourceSet#compileClasspath to 'compileOnly' configuration instead of 'compile', and possibly even auto-configure CompilerOptions#processorpath to the 'apt' configurations (or sourceSet's processorpath); but the code seems to be in the middle of the bridge to the new model and I'm not sure how and where to do all of that.

Then there are the IDE tasks, and the IDE models in the Tooling API (and then the IDE integrations themselves, but we're getting out of scope).

Thomas Broyer

unread,
May 8, 2015, 6:39:09 AM5/8/15
to gradl...@googlegroups.com, punya....@gmail.com


On Thursday, May 7, 2015 at 2:18:27 PM UTC+2, Thomas Broyer wrote:
So, it took me a bit of time (I had to learn nebula-test, Spock, and a bit more of Groovy, and I tried many many options) but I finally have something I'm happy with as a first step: https://github.com/tbroyer/gradle-apt-plugin/
(I cleaned up my history so you won't see the myriad of things I tried out)
Compared to what I was doing in my build scripts, I changed the compileOnly configurations to extend from 'compile' and assigned them as the sourceSets' compileClasspath (instead of only configuring the JavaCompile tasks' classpath).

I think I could easily add a processorpath, processors and processorArgs properties to JavaCompile's CompileOptions (similar to how I already added sourcepath), defaulting to an empty processorpath (possible breaking change, just assign processorpath=classpath to revert to previous/current behavior, just like we did with sourcepath).

Actually, it's probably better not to pass -processorpath when the processorpath is empty (so javac looks them up in the classpath) so we don't break backwards compatibility. Just pass -proc:none in compilerArgs to disable annotation processing altogether.
 
If processors is the empty list, then no -processor argument is passed to javac and the default discovery process is used.
processorArgs is a Map<String,?> and each entry is passed as -A<key>=<value>

Just sent a PR for the above: https://github.com/gradle/gradle/pull/450

Thomas Broyer

unread,
May 15, 2015, 9:26:23 AM5/15/15
to gradl...@googlegroups.com, punya....@gmail.com


On Tuesday, April 7, 2015 at 12:35:47 AM UTC+2, luke.daley wrote:
There’s a couple of interim steps here:

1. Add a `processorpath` property to CompileOptions (like the existing ‘sourcepath’)
2. Add a `processorpath` property to SourceSet (like existing ‘compileClasspath’)
3. Update the Java plugin to wire #2 to #1
4. Create default/conventional dependency configurations that are wired to #3
5. (more stuff I haven’t thought of)

We might want to do some strong modelling of the other annotation processor relevant options too. 
Having the IDEs support this is really dependent on #2. Getting to something like your example means #4.
That’s all reasonably straightforward stuff, if we could agree on #2 (i.e. the exact API changes).

If you’re interested in progressing this, the best thing to do would be to submit what we call a “design spec” with a proposal for these changes.

Thomas Broyer

unread,
Jun 9, 2015, 5:24:08 AM6/9/15
to gradl...@googlegroups.com, punya....@gmail.com
Ping. It's been 3½ weeks already, and zero feedback in either this thread or the pull request(s).

FWIW, my net.ltgt.apt plugin following the proposed design is used in prod since then, e.g.: https://github.com/ozwillo/ozwillo-kernel/blob/master/oasis-model/model.gradle (and I'm using the IntelliJ IDEA integration daily, on this and another projects)

Punyashloka Biswal

unread,
Jun 9, 2015, 8:04:48 AM6/9/15
to Thomas Broyer, gradl...@googlegroups.com
Hi Thomas,

I apologize - I'm not a regular Gradle contributor, and work has gotten in the way of making progress on my spec or commenting on your PR. I don't expect to be able to put a lot of time into this in the near future, unfortunately.

Regards,
Punya
Reply all
Reply to author
Forward
0 new messages