The Java Plugin (original) (raw)

Starting with Gradle 4.7, the incremental compiler also supports incremental annotation processing. All annotation processors need to opt in to this feature, otherwise they will trigger a full recompilation.

As a user you can see which annotation processors are triggering full recompilations in the --info log. Incremental annotation processing will be deactivated if a custom executable or javaHome is configured on the compile task.

Making an annotation processor incremental

Please first have a look at incremental Java compilation, as incremental annotation processing builds on top of it.

Gradle supports incremental compilation for two common categories of annotation processors: "isolating" and "aggregating". Please consult the information below to decide which category fits your processor.

You can then register your processor for incremental compilation using a file in the processor’s META-INF directory. The format is one line per processor, with the fully qualified name of the processor class and its case-insensitive category separated by a comma.

Example: Registering incremental annotation processors

processor/src/main/resources/META-INF/gradle/incremental.annotation.processors

org.gradle.EntityProcessor,isolating org.gradle.ServiceRegistryProcessor,dynamic

If your processor can only decide at runtime whether it is incremental or not, you can declare it as "dynamic" in the META-INF descriptor and return its true type at runtime using the Processor#getSupportedOptions() method.

Example: Registering incremental annotation processors dynamically

processor/src/main/java/org/gradle/ServiceRegistryProcessor.java

@Override
public Set<String> getSupportedOptions() {
    return Collections.singleton("org.gradle.annotation.processing.aggregating");
}

Both categories have the following limitations:

"Isolating" annotation processors

The fastest category, these look at each annotated element in isolation, creating generated files or validation messages for it. For instance an EntityProcessor could create a <TypeName>Repository for each type annotated with @Entity.

Example: An isolated annotation processor

processor/src/main/java/org/gradle/EntityProcessor.java

Set<? extends Element> entities = roundEnv.getElementsAnnotatedWith(entityAnnotation);
for (Element entity : entities) {
    createRepository((TypeElement) entity);
}

"Isolating" processors have the following additional limitations:

When a source file is recompiled, Gradle will recompile all files generated from it. When a source file is deleted, the files generated from it are deleted.

"Aggregating" annotation processors

These can aggregate several source files into one or more output files or validation messages. For instance, a ServiceRegistryProcessor could create a single ServiceRegistry with one method for each type annotated with @Service.

Example: An aggregating annotation processor

processor/src/main/java/org/gradle/ServiceRegistryProcessor.java

JavaFileObject serviceRegistry = filer.createSourceFile("ServiceRegistry");
Writer writer = serviceRegistry.openWriter();
writer.write("public class ServiceRegistry {");
for (Element service : roundEnv.getElementsAnnotatedWith(serviceAnnotation)) {
    addServiceCreationMethod(writer, (TypeElement) service);
}
writer.write("}");
writer.close();

Gradle will always reprocess (but not recompile) all annotated files that the processor was registered for. Gradle will always recompile any files the processor generates.