Gradle can not compile the code with the generated sources by annotationProcessor

3.8k views Asked by At

How to make the gradle build/compile a project using generated source by annotationProcessor?

I have a project with 2 modules

app-annotation -> is a module that defines one annotation and a successor of the AbstractProcessor

app-api -> contains Entities with annotation from the previous module

The idea was to generate default CRUD Repositories and Services for each entity, also if it is needed have the possibility to extend some service.

The problem is, it generates all the needed java files (even Intellij Idea see those files) but as soon as I want to extend one of the services it fails while it is being compiled because as I understand at the moment of compiling my class there is not its superclass which is generated after. If I do then recompile only my class then it is ok Moreover, eclipse somehow compiles with no error at all, only when I build with Idea or gradlew build.

To fix it the solution below is used, but it looks not very nice

configurations {
    preProcessAnnotation
}

def defaultSrcDir = "$projectDir/src/main/java"
def entitySources = "$projectDir/src/main/java/com/abcd/app/entity"
def generatedSources = "$buildDir/generated/sources/annotationProcessor/java/main"
def generatedOutputDir = file("$generatedSources")

// Explicitly run the annotation processor against the entities
task preProcessAnnotation (type: JavaCompile) {
    
    source = entitySources
    classpath = sourceSets.main.compileClasspath
    destinationDirectory = generatedOutputDir
    options.sourcepath = sourceSets.main.java.getSourceDirectories()
    options.annotationProcessorPath = configurations.getByName("preProcessAnnotation") 
    
    options.compilerArgs << "-proc:only"    
    options.encoding = "ISO-8859-1"
}

// Explicitly specify the files to compile
compileJava {
    dependsOn(clean)
    dependsOn(preProcessAnnotation)

    def files = []
    fileTree(defaultSrcDir).visit { FileVisitDetails details -> 
        files << details.file.path 
    }
    fileTree(generatedSources).visit { FileVisitDetails details -> 
        files << details.file.path 
    }   
    source = files
    
    options.compilerArgs << "-Xlint:deprecation"
    options.compilerArgs << "-Xlint:unchecked"
    options.encoding = "ISO-8859-1"
}
....

dependencies {

    preProcessAnnotation project(':app-annotation')

    // Generate the crud repositories and services
    compile project(':app-annotation')
    implementation project(':app-annotation')
    ...
}

I am just curious how similar frameworks of code generation such as Lombok, Dagger2 work without any problem.

PS. I feel it should be much simpler, doesn't it?

2

There are 2 answers

0
Yuko On BEST ANSWER

Eventually, all it is my mistake, I used wrong method to save a generated java file.

// Wrong!
javax.annotation.processing.Filer#createResource(StandardLocation.SOURCE_OUTPUT, packageName, className + ".java")
// Correct
javax.annotation.processing.Filer#createSourceFile(packageName + "." + className)
0
PrasadU On

As you realised - any code generation must be done before compile.

A cleaner approach is to have your code generation logic / annotation as dependency.

task codeGen {
    // creates under build/genSrc
}

//add to default source set
sourceSets.main.java.srcDir "build/genSrc"

//this has access to all code
compile.dependsOn codeGen