Search

Making incremental KAPT work (Speed Up your Kotlin projects!)

Originally published on the author’s private blog.

Do you have kapt in your build.gradle dependencies? Are you using Dagger, Glide, Room? Chances are you can make your project build much faster by enabling incremental annotation processing.

Annotation processors are supported in Kotlin with the kapt compiler plugin. Popular libraries like Dagger or Data binding are using it. The kapt compiler did not support incremental processing until recently — when Kotlin 1.3.30 was released (April 12, 2019). This meant that any change in a given module would result in processing the whole module again in each build. This would lead to unnecessarily very slow builds for larger modules.

Incremental kapt doesn’t work out of the box. There are three conditions that need to be met:

  • Use Kotlin 1.3.31 or newer
  • Enable incremental kapt by adding kapt.incremental.apt=true in your project’s gradle.properties
  • Make sure that all annotation processors in the module support incremental kaptThe whole Gradle module will be non-incremental if there is just one non-incremental annotation processor. This also means that you can move code with non-incremental Kotlin annotations to separate modules to speed up builds.

How to detect non-incremental annotation processors in your project?

You could manually go trough all of your project’s modules and check the dependencies. For each kapt entry you could then check if it supports incremental kapt (e.g. there is a list here). But the easiest way is to check the Gradle logs.

Make sure you have kapt.use.worker.api=true in your gradle.properties (just add it for the short time if you don’t) and run:

./gradlew aDeb -Pkapt.verbose=true

Search for the string “Processors that are not incremental”in the log output. This will list the non-incremental processors in each module. This is a good starting point.

Note: You can also get this output without having kapt.use.worker.api=true but you need to run it with --info flag — which creates a lot of output.

What to do if you find non-incremental annotation processors in your modules?

The first step is to check that you are using the latest version of that library. A lot of libraries have just recently added support for incremental annotation processors (Realm, Dagger, …). So you can maybe make most of the dependencies support incremental processing just by updating them.

Some libraries require you to manually enable it. For example Dagger requires you to add this each of your module:

android {
   defaultConfig {
      javaCompileOptions {
         annotationProcessorOptions {
            arguments << ["dagger.gradle.incremental": "true"]
         }
      }
   }
]Code language: Gradle (gradle)

Note: There are several ways for Dagger if you search for it, some work and some not — this one is guaranteed to work :-).

There are some popular libraries that still don’t support incremental annotation processing. For example Room or Lifecycle from the Android Architecture Compontents. Or Epoxy. There is not much you can do — except for moving the Kotlin classes with annotations into a separate Gradle module. This way only a small module will have to be re-processed on each build.

The impact on build performance can be huge — one of our bigger projects at Avast went down from 3 minutes to 1 minute build time for incremental builds. It’s more noticeable if you have larger Gradle modules or a monolithic single module project.

More ways how to speed up annotation processing

There are other options you can also explore to speed up the processing. There is a good summary written at https://kotlinlang.org/docs/reference/kapt.html. You should definitely enable kapt build cache and try to enable parallel kapt processing.