Gradle+apt: A separate generateSources task to execute before compiling

Viewed 585

I use gradle-apt-plugin for configuring annotation processing. It does this by configuring JavaCompile (and GroovyCompile) tasks. As a result, annotation processing happens as part of every compile task.

For several reasons I would like annotation processing to happen in separate tasks (one for each source set) and make the compile tasks depend on them. For example: generateSources, generateTestSources, generateApiSources, etc

Rationale

I'm hoping to see a few benefits from generating and compiling as separate build stages, mainly to reduce secondary compiler errors. I.e. it would be nice if the build fails early during annotation processing, without reporting all the additional errors that come from regular sources that depend on generated classes that didn't generate because of an error.

For instance: it often happens that Dagger fails with an error, which causes Immutables to not generate classes, which causes a whole bunch of secondary errors to wade through. I'm hoping the compile task will never execute because generateSources has failed.

But I'd also like generated sources to be formatted for easier debugging in case of apparent problems with a processor (or my use thereof), and apply license headers on generated sources.

What I've tried

So far, I've been able to make it generate without compiling by adding the code below to my buildscript and passing the procOnly property:

compileJava.doFirst {

    if (project.hasProperty("procOnly")) {

        logger.lifecycle("Option procOnly detected - Processing annotations only, without compilation (-proc:only)")
        options.compilerArgs << '-proc:only'
    }
}

What I'm trying

Invoking tasks from another tasks is deprecated, and now I'm thinking along the lines of duplicating each JavaCompile task, then configuring the regular compile tasks to use the -proc:none argument and the duplicated tasks (i.e. the generate tasks) to use the -proc:only argument.

This answer by Peter Niederwieser shows how this can be done. The properties of JavaCompile are all settable, and this is what I thought should work:

task generateSources(type: JavaCompile) {

    // ====  Copy properties from corresponding compile task  =====
    classPath = compileJava.classPath
    destinationDir = compileJava.destinationDir
    excludes = compileJava.excludes
    includes = compileJava.includes
    options = compileJava.options
    source = compileJava.source
    sourceCompatibility = compileJava.sourceCompatibility
    targetCompatibility = compileJava.targetCompatibility
    toolChain = compileJava.toolChain

    // ====  Configure this compile task to process only  =====
    options.compilerArgs << '-proc:only'

    // ====  Configure the main compile task to not process at all  =====
    compileJava.options.compilerArgs << '-proc:none'
}

project.afterEvaluate {

    // ====  Ensure sources are generated before compiling  =====
    compileJava.dependsOn("generateSources")
}

However, the build fails: Error:(300, 0) Could not get unknown property 'classPath' for task ':compileJava' of type org.gradle.api.tasks.compile.JavaCompile. My experience with Gradle and Groovy is still limited at this point, but something like sourceSets.main.output.generatedSourcesDir does work. Why not this though?

Question

Is this the right approach and if so, how can I copy the compile task? I'm particularly interested in ensuring that incremental builds still work.

If not the right approach, what can I try instead?

0 Answers
Related