On Fri Feb 20 2015 at 21:11:14 Sébastien Lesaint < sebastien.lesa...@gmail.com> wrote:
> Hello, > > I have been into annotation processing a lot for the past year and a half > and I am now going back to the basics so that I can share knowledge I > gathered. > > Doing so, I stumbled upon, again, a problem I had meet very early, for > which I had googled the solution, but this time I went deeper to figure out > what was really wrong. > > # the symptoms > > When writing an annotation processor and creating the associated > service-discovery file > "`META-INF/services/javax.annotation.processing.Processor" in a Maven > module, one needs to disable annotation processing completely (using > the <proc>none</proc> configuration option of the maven-compiler-plugin) > otherwise the module compilation succeeds but not a single .class file is > created. > > # how I investigated > > I compiled the module with the -X option, extracted the javac command line > and ran it by hand. I got the following compilation error: > error: Bad service configuration file, or exception thrown while > constructing Processor object: javax.annotation.processing.Processor: > Provider com.foo.BarProcessor not found > > The thing is, the com.foo.BarProcessor is the very processor I am compiling > (and the single .java file of the module). > > # what I found > > I went through the maven logs, spent some time thinking about it and > believe that we are facing one, maybe two bugs in the > maven-compiler-plugin: > > 1. Maven copies resources from the "src/main/resources" directory to the > "target/classes" directory > 2. the maven-compiler-plugin builds up a javac command line that specifies > the "target/classes" directory as a member on the classpath with the > argument "-classpath" (is that a bug ? there may be a good reason to do so > but in a basic jar module setup I can see any) > This is because of m-compiler-p's attempt at incremental compilation. It looks at all source files and if one doesn't have a corresponding *.class file or is newer then it adds it to the list of source files to recompile. Because those will likely reference other classes that might not have changed, target/classes is included in the classpath so that the already-compiled classes can be found. That said, incremental compilation is (was) kind-of broken, as it doesn't track class dependencies and won't recompile those classes that reference the classes that have changed. See https://cwiki.apache.org/confluence/display/MAVEN/Incremental+Builds for an example. If you ask me, m-compiler-p should just check whether "something" needs to be recompiled (which in itself is already error-prone, for many reasons [1]) and recompile everything. Actually, m-compiler-p 3.1+ should apparently behave that way by deleting all *.class files it had created in a previous run (unless you set useIncrementalCompilation=false [2]), so it shouldn't need target/classes to be part of the classpath, at least for this use-case of incremental compilation, but still includes it anyway. Takari has a new plugin (and lifecycle) that apparently does truly incremental compilation [3], by tracking class dependencies (when using JDT rather than javac). It'd still fail your use-case though IIUC, if it supported annotation processing through JDT (I haven't looked at how it handles incremental builds with javac). The other reason for target/classes to be in the classpath is so that annotation processors could find resources (let's not debate here whether it's a good idea or not). m-compiler-p developers can't seem to agree on what should be done: should m-compiler-p use -sourcepath sometimes [4] or always [5] ? Should target/classes be included in the sourcepath [6] ? [1] http://blog.ltgt.net/maven-is-broken-by-design/ (note: things have changed a bit since I wrote this; e.g. incremental build in m-compiler-p 3.1+, the Takari lifecycle, etc.) [2] https://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html#useIncrementalCompilation [3] http://takari.io/2014/10/16/incremental-compilation.html [4] https://jira.codehaus.org/browse/MCOMPILER-26, https://jira.codehaus.org/browse/MCOMPILER-174 [5] https://jira.codehaus.org/browse/MCOMPILER-98 [6] https://jira.codehaus.org/browse/MCOMPILER-122 3. when run, javac finds a > "META-INF/services/javax.annotation.processing.Processor" file in the > classpath and looks for the specified processor "com.foo.BarProcessor " > 4. obviously, this processor can not be found, javac reports an error and > stops the compilation before it even started compiling anything (ence not a > single .class file in the target directory) > 5. the error ("error: Bad service configuration file, or exception thrown > while constructing Processor object: javax.annotation.processing. > Processor: > Provider com.foo.BarProcessorr not found") is ignored by the > maven-compiler-plugin that considers the compilation successful and the > maven build goes on (this is our big bug) > > # bonus > > Please note that this has pernicious side effects. > > Since the build is successful, the jar which is supposed to contain the > processor is created but only with the resources which means that it > contains the "META-INF/services/javax.annotation.processing.Processor" > file. > > Now, any module or project which has this jar file as a dependency will > produce 0 .class file. Their compilation classpath is "contaminated" with a > "META-INF/services/javax.annotation.processing.Processor which references > an non existent processor. Javac will therefor fail with the "Bad service > configuration file" and produces no .class file. > > # affected versions > > I tested this with Maven 3.1.1 & 3.2.5, maven-compiler-plugin 2.5, 3.1 and > 3.2 and Oracle JDK 6, 7 & 8 on a ubuntu 64 bits machine. But, according to > the various posts, stackoverflow entries, etc. all around the web which > provide the <proc>none</proc> solution, it occurs on other setups. > > # conclusion > > I initially though I would file a bug in the maven-compiler-plugin JIRA but > it is unclear how I can create an account. > It's a known bug, opened for YEARS: https://jira.codehaus.org/browse/MCOMPILER-97 Note that a workaround might be to generate the META-INF/services/javax.annotation.processor.Processor file using annotation processing ;-) using either com.google.auto.service:auto-service, org.kohsuke.metainf-services:metainf-services, or org.immutables:metainf (or anything similar)