How to get all elements with an annotation in an IntelliJ incremental build?

Dan Berindei :

I'm writing an annotation processor which needs to collect all the classes with a certain annotation in the current module and write a class referencing each of them.

To simplify a bit, given these source files:

src/main/java/org/example/A.java
@Annotation
class A {}
src/main/java/org/example/B.java
@Annotation
class B {}

I want to generate a class:

target/generated-sources/org/example/Module.java
class Module {
  String getModuleClasses() {
    return Arrays.asList(
      "org.example.A",
      "org.example.B"
    );
  }
}

This works from Maven, but when I modify class A, IntelliJ gives my annotation processor a RoundEnvironment with A as the single root element.

I understand Gradle supports incremental compilation with aggregating annotation processors by passing a fake RoundEnvironment with all the sources matching the annotations to the annotation processor, but IntelliJ doesn't seem to have anything similar. (Except maybe for Gradle projects?)

What would be the best way to find both classes when IntelliJ compiles only class A?

Maybe the annotator could keep a list of annotated classes: read the list from a resource file in the first round, in each round remove from the list root elements and add to the list elements which are annotated, and write the list back to the resource file in the final round?

Ahmad Bawaneh :

One way to solve this issue is use some sort of a registry in between builds, you can for example store the types annotated in a service like style in meta-inf

So in your processor you defer the code generation until the processing last round, and after generating your code you store the types in a file into a file under the META-INF

FileObject resource = processingEnv.getFiler()
                    .createResource(StandardLocation.CLASS_OUTPUT, "", "META-INF/annotatedtypes/"+fileName);
PrintWriter out = new PrintWriter(new OutputStreamWriter(resource.openOutputStream()));
            classes.forEach(out::println);

You need to check for duplicate entries of course.

At some point before generating the code, read the types and generate your code based on that

FileObject resource = processingEnv.getFiler()
                        .getResource(StandardLocation.CLASS_OUTPUT, "", "META-INF/annotatedtypes/"+fileName);
new BufferedReader(new InputStreamReader(resource.openInputStream())).lines().forEach(classes::add);

the file content could look like something like this

org.foo.bar.A
org.foo.bar.B

The problem with this is that when you defer the code generation to the last round your generated code will not be picked by any other processor for example dagger2, and also sometime the file might end with records for classes that does not exist anymore.

In summary inside your processor you do the following

  • Read the registered types from the file at META-INF
  • Get elements annotated with your annotation.
  • If it is the last round you just update the file with unique records set and generate code.

You read the file every round, but writes only once at the last round.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=96393&siteId=1