google / ksp

Kotlin Symbol Processing API
https://github.com/google/ksp
Apache License 2.0
2.84k stars 266 forks source link

SymbolProcessor resolver not returning all annotated files when changing only one annotated file #1370

Closed ParticleCore closed 1 year ago

ParticleCore commented 1 year ago

Currently trying to generate a simple file that contains a list of all the classes annotated with a specific annotation, but failing to get more than the changed files in resolver.getAllFiles.

This is what I am trying to achieve:

  1. 5 files are annotated with @MyAnnotation
  2. Build project
  3. SymbolProcessor runs and resolver.getAllFiles size is 5, containing the 5 files mentioned above
  4. I annotate an extra file and build project
  5. SymbolProcessor runs and resolver.getAllFiles size is 6, containing all the 6 files annotated above

What is actually happening

  1. 5 files are annotated with @MyAnnotation
  2. Build project
  3. SymbolProcessor runs and resolver.getAllFiles size is 5, containing the 5 files mentioned above
  4. I annotate an extra file and build project
  5. SymbolProcessor runs and resolver.getAllFiles size is 1, containing only the last file changed above

I was not able to find any explanation for this behavior nor how to overcome it. This behavior appears to be incremental changes only and I cannot begin to explain how having to work with that limitation for the purpose I described above would be an absolute nightmare to work with, and very likely bound to not work correctly in the end especially considering there is no way of knowing when the list is starting from 0 (assuming the processor had changes) vs running on the lasted changed files only.

How can I obtain a list of all the annotated files the resolver already processed when only one file is annotated instead of only getting the newest annotated file(s)?

ParticleCore commented 1 year ago

Just stumbled on this while checking the source code:

/**
 * Get all files in the module / compilation unit.
 *
 * @return all input files including generated files from previous rounds, note when incremental is enabled, only dirty files up for processing will be returned.
 */
fun getAllFiles(): Sequence<KSFile>

Does this mean there is absolutely no other way to get all the files without turning off incremental?

Adding ksp.incremental=false to gradle.properties fixes my problem, but concerned this will affect any ksp from other modules/libraries when there could be a better alternative that I am not aware of.

neetopia commented 1 year ago

The reason for this is that those files not returned are not required for processing/compilation. If you need such files as part of your processing, you should declare these files as dependencies to your generated symbols so that Gradle includes them when calculating dirty files. Note that this behavior is controlled by Gradle so there is no way for KSP to get around it.

ParticleCore commented 1 year ago

Forgive my ignorance, but since I cannot find any detailed, or better yet, clear documentation about that part I would like to ask if by "declare these files as dependencies" you mean I should do this:

https://github.com/google/ksp/blob/1b5864da2cb1a9a9fa8e227aaed0c6508270c969/common-util/src/test/kotlin/com/google/devtools/ksp/processing/impl/CodeGeneratorImplTest.kt#L62

or something like this:

https://github.com/google/ksp/blob/e488d76f7c8145728d62f9858ff6aa886fa6b8eb/examples/playground/test-processor/src/main/kotlin/BuilderProcessor.kt#L32

where I would include in that dependency the list of files I want to keep being returned/included whenever the processor runs again?

neetopia commented 1 year ago

Simply running a processor again won't trigger a new build if you don't make any changes to the source code.

Your first snippet will trigger a full rebuild if any changes are made, while your second snippet will only trigger a rebuild when the containing file is modified or when new files are added.

Based on your question in your original post, you want to get all source files when a new file is added, so you should go with the first option.

ParticleCore commented 1 year ago

Thanks for taking the time to explain this. I tested both scenarios:

createNewFile(Dependencies(true, sourceFiles), targetPackage, fileName)
createNewFile(Dependencies.ALL_FILES, targetPackage, fileName)

With ksp incremental turned on

ksp.incremental=true

And in both cases all the files that I need were available in the resolver whenever an annotation was added to a new class or removed from an existing one.

Since the first example worked for what I need I think I will use that one since it seems to me like it's more conservative given that the list of the sourceFiles I am interested in is being included, although to be honest I am not sure if I am interpreting anything about this correctly.

Since this issue was a non-issue, instead a user ignorance issue I will go ahead and close this.

If you have any other information you believe might be useful to add please feel free to contribute, and thanks once again for helping me out with this. Hopefully this will be helpful for other devs that come across this scenario.