Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make my own annotation processor incremental?

I've created an annotation processor called EasyPrefs and when I try to use it on my projects, it shows the following warning.

Incremental annotation processing requested, but support is disabled because the following processors are not incremental: net.androidcart.easyprefs.EasyPrefsProcessor (NON_INCREMENTAL).

I did some research and could not find any tutorials on how to make it incremental. Are there any Gradle configurations needed, or some functions need to be overridden, etc.

like image 673
Amin Avatar asked Nov 21 '19 01:11

Amin


1 Answers

To make your annotation processor incremental you must declare it in META-INF in incremental.annotation.processors file:

your.fully.qualified.annotation.processor.name,category

There are three categories of annotation processor to choose from: dynamic, isolating and aggregating.

Basically at a high-level:

  • dynamic: when your processor can only decide at runtime whether it is incremental or not
  • isolating: when your processor will work with each type annotated with your annotation in isolation (one input for one or more output)
  • aggregation: when your processor need aggregate several inputs (type annotated with your annotation) to making one or more output

But, each category has limitations that you have to consider:

Dynamic limitations

  • They must generate their files using the Filer API. Writing files any other way will result in silent failures later on, as these files won’t be cleaned up correctly. If your processor does this, it cannot be incremental.

  • They must not depend on compiler-specific APIs like com.sun.source.util.Trees. Gradle wraps the processing APIs, so attempts to cast to compiler-specific types will fail. If your processor does this, it cannot be incremental, unless you have some fallback mechanism.

  • If they use Filer#createResource, the location argument must be one of these values from StandardLocation: CLASS_OUTPUT, SOURCE_OUTPUT, or NATIVE_HEADER_OUTPUT. Any other argument will disable incremental processing.

Isolating limitations

  • They must make all decisions (code generation, validation messages) for an annotated type based on information reachable from its AST. This means you can analyze the types' super-class, method return types, annotations etc., even transitively. But you cannot make decisions based on unrelated elements in the RoundEnvironment. Doing so will result in silent failures because too few files will be recompiled later. If your processor needs to make decisions based on a combination of otherwise unrelated elements, mark it as "aggregating" instead.

  • They must provide exactly one originating element for each file generated with the Filer API. If zero or many originating elements are provided, Gradle will recompile all source files.

Aggregation limitations

  • They can only read CLASS or RUNTIME retention annotations

  • They can only read parameter names if the user passes the -parameters compiler argument.

From Gradle documentation

PS: for dynamic, you also have to override getSupportedOptions() method to specify the category: isolating or aggregation. See the Gradle documentation for more details.

like image 81
Flavio Andrade Avatar answered Oct 16 '22 19:10

Flavio Andrade