Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Annotation processor in Gradle outputs source files to build/classes making javadoc fail. How to fix it?

I have an annotation processor that is automatically picked up by the Java compiler at build time (using SPI). During a gradle build, the generated java sources of this annotation processor are put in build/classes as Gradle tells the annotation processor that this is the place to output generated source files.

When the standard javadoc task is run, it tries to create javadoc for all files in build/classes, including *.java. This failes because javadoc only expects *.class files, making the whole build fail.

So my question is:

Is this a Gradle bug/feature? How do I fix it/make it work?

like image 988
Zubzub Avatar asked Aug 11 '14 08:08

Zubzub


1 Answers

It seems the problem is that the generated source files are not picked up, making the javadoc fail because it had nothing to process.

I'm posting the solution here in case somebody is experiencing the same problem:

The problem with compile time source generation in gradle is that the outputted sources are not automatically picked up by the javadoc. This is a problem if all your sources are auto generated. The build will fail with an error saying that no sources could be processed. In the other case your build will succeed but you will have no javadoc of your generated java sources.

The root problem here is gradle's poor integration with generating sources that are both generated and compiled during the same compile step. To remedy this I changed my build files to this.

project layout:

  • rootproject
  • rootproject/annotationProcessor
  • rootproect/userOfAnnotationProcessor

build file of userOfAnnotationProcessor

def generatedSources = "$buildDir/generated-src"
def generatedOutputDir = file("$generatedSources")

compileJava {
    doFirst {
        generatedOutputDir.exists() || generatedOutputDir.mkdirs()
        options.compilerArgs = [
                '-s', "${generatedSources}"
        ]
    }
}


sourceSets {
    main {
        java {
            srcDirs += generatedOutputDir
        }
    }
}

javadoc {
    source = sourceSets.main.resources
}
compileJava.dependsOn clean

The trick here is to not add your generated sources to a custom sources set, else we'll run into troubles when trying to build aggregated javadoc in our root project. However this solution has the nasty side effect that our generated sources or added twice for some reason when trying to build after a first clean+build. The solution here is to always do a clean+build.

Now when doing an aggregate javadoc build, we'd like our generated source javadoc to be part of it as well.

This is how our rootproject build file looks like:

def exportedProjects = [
        ":annotationProcessor",
        ":userOfAnnotationProcessor",
]

task alljavadoc(type: Javadoc) {
    source exportedProjects.collect { project(it).sourceSets.main.allJava }
    classpath = files(exportedProjects.collect { project(it).sourceSets.main.compileClasspath })
    destinationDir = file("${buildDir}/docs/javadoc")
}


alljavadoc.dependsOn(":userOfAnnotationProcessor:compileJava")

If we had used a custom source set previously, gradle would now start complaining about source set properties not being found. Why? I don't know... A last important thing to notice is that our alljavadoc depeonds on the compilation step of userOfAnnotationProcessor, this is needed to make sure our generated source files are there when the aggregated javadoc is build.

I hope I've helped sombody with this explanation!

like image 108
Zubzub Avatar answered Oct 19 '22 09:10

Zubzub