Incremental processing
KSP supports incremental processing: KSP reprocesses a file only when one or more of its dependencies change. This avoids unnecessary reprocessing and therefore reduces compilation time.
Incremental processing is enabled by default. You can disable it when troubleshooting or when you need to force a full rebuild. To disable it, add the following line to your gradle.properties file:
Dirty files
A file is considered dirty (needing to be reprocessed) if it is either directly modified by a developer or indirectly affected by changes in other dirty files.
To determine which sources are dirty, KSP relies on processors, which associate generated outputs with their corresponding input sources. KSP uses these associations to identify the sources that must be reprocessed when a change occurs.
KSP requires only a minimal set of root sources. Processors use these sources as entry points to navigate the code structure.
A root source is a source file whose symbols are obtained directly from any of the following methods:
Resolver.getAllFiles()Resolver.getSymbolsWithAnnotation()Resolver.getClassDeclarationByName()Resolver.getDeclarationsFromPackage()
A processor can obtain additional symbols from other source files by resolving information from a root source. KSP automatically tracks these dependencies.
When generating an output, the processor must declare the root sources that contribute to that output. KSP uses those root sources and their tracked dependencies to determine when the output needs to be regenerated.
Aggregating and isolating outputs
KSP classifies generated outputs into two types: aggregating and isolating.
- Aggregating
Aggregating outputs can be affected by changes in any source file, except for removals that do not impact other files.
Any input change triggers a rebuild of all aggregating outputs and reprocessing of all corresponding registered, new, or modified source files.
For example, an output that collects all symbols with a particular annotation is aggregating.
- Isolating
Isolating outputs depend only on their specified sources.
Changes to other sources do not affect the output. Multiple source files can be associated with a single output.
For example, a generated class that is dedicated to an interface it implements is isolating.
Dirtiness propagation
KSP propagates dirtiness in the following ways:
By resolution tracing: Type resolution is the only way to traverse from one file to another. When a processor resolves a type reference (explicitly or implicitly), KSP considers dependencies between the file containing the reference and any file that defines a symbol affecting that resolution. As a result, a change in a resolved symbol may mark the referencing file as dirty.
By input-output correspondence: If a source file is changed or affected, all other source files that share generated outputs with it are also marked as affected. This groups related files into equivalence classes based on shared outputs.
Implementation
Dependencies are determined by the many-to-many relationship between input and output files.
This is how KSP determines which files need to be reprocessed:
If an input file is changed, it will always be reprocessed.
Why? If an input is changed, new information can be introduced. Processors need to run again with the input.
If an input file is changed and is associated with an output, then all other input files associated with the same output will also be reprocessed. This happens repeatedly until there is no new dirty file.
Why? An output is made out of a set of inputs. Processors may need all the inputs to regenerate the output.
If an unchanged input file isn't associated with any aggregating outputs, it won't be reprocessed.
Why? This file can't affect any outputs because it is unchanged and isn't associated with an aggregating output. It won't be reprocessed unless one of the above rules applies.
For example, consider a project with the following structure:
A processor:
reads
sourceA.generates
outputA.reads
sourceB.generates
outputB.
When sourceA changes:
If
outputBis aggregating, KSP reprocesses bothsourceAandsourceB.If
outputBis isolating, KSP reprocessessourceAonly.
If sourceC is added:
If
outputBis aggregating, KSP reprocessessourceCandsourceB.If
outputBis isolating, KSP reprocesses onlysourceC.
If either sourceA or sourceB is removed, KSP doesn't need to reprocess any files.
Example processor
The following project contains classes A and B, where A extends B:
To generate outputForA, the processor:
gets A by calling
Resolver.getSymbolsWithAnnotation.gets B by calling
KSClassDeclaration.superTypeson A.
KSP tracks this relationship through resolution tracing and automatically records B as a dependency of A. Therefore, you don't need to explicitly declare B.kt as a dependency of outputForA.
Reporting bugs
If you encounter any error that occurs only when incremental processing is enabled, create an issue in the GitHub repository and attach the relevant log files.
Enable incremental processing logs by adding the following line to
gradle.properties:ksp.incremental.log=truePerform a clean build that completes successfully.
Save the generated log files by copying them to another location:
build/kspCaches/<source set>/logs/kspDirtySet.logbuild/kspCaches/<source set>/logs/kspSourceToOutputs.log
Modify a source file that triggers the issue and run the build again.
Attach the log files from both the successful build and the build that reproduces the issue to the GitHub issue.