WordPress-FluxC-Android icon indicating copy to clipboard operation
WordPress-FluxC-Android copied to clipboard

Add support for incremental annotation processing

Open oguzkocer opened this issue 3 years ago • 0 comments

This PR adds incremental annotation processing support by updating com.google.auto.service:auto-service & wellsql and adding the necessary fluxc-processor/src/main/resources/META-INF/gradle/incremental.annotation.processors file.

The tricky bit for enabling incremental annotation processing is not the work itself but rather making the correct category choice between isolating, aggregating & dynamic and making sure that our processors don't violate any of the rules for incremental annotation processing support. All of these are covered in Gradle's Incremental annotation processing documentation.

Even though I've completed this work a while back, I wanted to gain more confidence in the changes by running various tests in all 4 repositories Wellsql, FluxC, WPAndroid & WCAndroid. These tests were certainly helpful, but I still don't feel 100% confident mostly due to a lack of verifying changes - or rather the possibility of silent failures which is also mentioned in Gradle's documentation. I am hoping this is where the peer review will be very helpful.

As a first step, I am opening draft PRs in wellsql & FluxC repos so that we can discuss these changes. If everything looks good, I'll also open the PR in WPAndroid and once we verify everything works as expected, we can merge the PRs in and enable incremental annotation processing in Wellsql, FluxC, WPAndroid & WCAndroid repos.


I strongly suggest going through the Gradle documentation - possibly a few times - while reviewing these PRs. Here is my basic understanding of each category:

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.

As far as I can tell, it a processor is not overriding the Processor#getSupportedOptions() it won't need to be dynamic because that's where we tell whether we support incremental annotation processing or not, depending on some outside factors.

Here is an example from Butterknife repository where dynamic category is used:

@Override public Set<String> getSupportedOptions() {
  ImmutableSet.Builder<String> builder = ImmutableSet.builder();
  builder.add(OPTION_SDK_INT, OPTION_DEBUGGABLE);
  if (trees != null) {
    builder.add(IncrementalAnnotationProcessorType.ISOLATING.getProcessorOption());
  }
  return builder.build();
}

As far as I can tell we don't have any processors that needs to be dynamic.

Isolating

The fastest category, these look at each annotated element in isolation, creating generated files or validation messages for it.

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.

Wellsql's TableProcessor and FluxC's ActionProcessor seems to fit this description. As far as I can tell, there is one originating source file in each case. TableProcessor creates multiple files, **Table.java & **Mapper.java, but I don't think the number of generated files is a factor.

Aggregating

These can aggregate several source files into one or more output files or validation messages.

"Aggregating" processors have the following limitations:

  • They can only read CLASS or RUNTIME retention annotations
  • They can only read parameter names if the user passes the -parameters compiler argument.

FluxC's EndpointProcessor seems to fit this description which processes multiple source files.


To test:

Before you start testing, please make sure to add the following properties to your gradle.properties file (or dynamically pass them in each build):

kapt.incremental.apt=true
kapt.use.worker.api=true
kapt.verbose=true

I suggest running various scenarios you can think of that'll require incremental annotation processing and verify the result. Here are a few examples:

EndpointProcessor:

  • Add a new endpoint to fluxc-processor/src/main/resources/wp-com-endpoints.txt and run ./gradlew :fluxc:assembleDebug and verify that fluxc/build/generated/source/kapt/debug/org/wordpress/android/fluxc/generated/endpoint/WPCOMREST.java includes the new endpoint.
  • Change an endpoint in fluxc-processor/src/main/resources/wp-com-endpoints.txt and run ./gradlew :fluxc:assembleDebug and verify that fluxc/build/generated/source/kapt/debug/org/wordpress/android/fluxc/generated/endpoint/WPCOMREST.java includes the changed endpoint.

Make sure to run these tests when there is already a generated file so that incremental annotation processing can be tested.

TableProcessor:

  • Add a new column to PostModel and run ./gradlew :fluxc:assembleDebug. Verify that fluxc/build/generated/source/kapt/debug/com/wellsql/generated/PostModelMapper.java & fluxc/build/generated/source/kapt/release/com/wellsql/generated/PostModelTable.java are updated accordingly.
  • Add a new constraint to PostModel and run ./gradlew :fluxc:assembleDebug. Verify that fluxc/build/generated/source/kapt/debug/com/wellsql/generated/PostModelMapper.java & fluxc/build/generated/source/kapt/release/com/wellsql/generated/PostModelTable.java are updated accordingly.
  • Add a new column to LikeModel and run ./gradlew :fluxc:assembleDebug. Verify that fluxc/build/generated/source/kapt/debug/com/wellsql/generated/LikeModelTable.java & fluxc/build/generated/source/kapt/debug/com/wellsql/generated/LikeModelTable.java are updated accordingly.

Make sure to run these tests when there is already a generated file so that incremental annotation processing can be tested.

ActionProcessor:

  • Add a new action to PostAction and run ./gradlew :fluxc:assembleDebug. Verify that fluxc/build/generated/source/kapt/debug/org/wordpress/android/fluxc/generated/PostActionBuilder.java includes the new action.

Make sure to run this test when there is already a generated file so that incremental annotation processing can be tested.

oguzkocer avatar Jun 24 '22 22:06 oguzkocer