tschuchortdev / kotlin-compile-testing

A library for testing Kotlin and Java annotation processors, compiler plugins and code generation

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Files generated through KSP not showing

gfreivasc opened this issue · comments

I'm having a problem with compiler testing. For some reason, it does not detect KSP generated files. They have been successfully generated and it's possible to browse to them through the file system, but they're not reported on the compilation result. It looks like it's looking for kapt generated stuff instead.

Here's how I'm running it:

compile(folder: File, vararg sourceFiles: SourceFile): KotlinCompilation.Result {
  val processors = provider.provide()
  val kspProcessors = processors.filterIsInstance<SymbolProcessor>()
  val kaptProcessors = processors.filterIsInstance<BasicAnnotationProcessor>()
  return KotlinCompilation()
    .apply {
      workingDir = folder
      inheritClassPath = true
      if (kspProcessors.isNotEmpty()) {
        symbolProcessors = kspProcessors
      } else if (kaptProcessors.isNotEmpty()) {
        annotationProcessors = kaptProcessors.asProcessorList
      }
      sources = sourceFiles.asList()
      verbose = false
      kspIncremental = true
    }.compile()
}
// ... during test
val result = compile(
  temporaryFolder.root,  // The jUnit rule for temporary folders
  SourceFile.kotlin(
    "source.kt",
    """
        package test
   
        import com.gabrielfv.crane.annotations.RoutedBy
        
        class R
        
  	@RoutedBy(R::class)
  	class A
    """
  )
)

The variable result won't contain no generated sources, all the properties generatedFiles, generatedStubFiles, sourcesGeneratedByAnnotationProcessor and compiledClassAndResourceFiles return empty for my KSP round. Everything works just fine for my KAPT rounds.

I am able to reproduce your issue. Strictly speaking it's "working as intended". Unfortunately, the current implementation of KotlinCompilation is quite limited in its extensibility and KSP, being implemented as a third-party extension instead of being integrated like KAPT, can not add its generated files to the Result class. Let me think of a way to gather all the new files in the working directory and I will add it as a new method to the Result class.

I see, so not a bug but rather that it's not designed to fully support KSP at least just yet. Other than this kind of behavior, what else do you think would be necessary to extend KSP support, and do you intend to do it in the sorter term?

I've been able to apply the following workaround:

internal val KotlinCompilation.Result.workingDir: File get() =
  outputDirectory.parentFile!!

val KotlinCompilation.Result.kspGeneratedSources: List<File> get() {
  val kspWorkingDir = workingDir.resolve("ksp")
  val kspGeneratedDir = kspWorkingDir.resolve("sources")
  val kotlinGeneratedDir = kspGeneratedDir.resolve("kotlin")
  val javaGeneratedDir = kspGeneratedDir.resolve("java")
  return kotlinGeneratedDir.listFilesRecursively() +
    javaGeneratedDir.listFilesRecursively()
}

This allows me to test for what I want without problems. Perhaps a similar strategy could be adopted for the KSP extensions? I could open a PR if you'd believe it to be applicable.

Here's how I'm testing this:

assertThat(result.exitCode)
  .isEqualTo(KotlinCompilation.ExitCode.OK)
val generated = if (backend == "ksp") {
  assertThat(result.kspGeneratedSources)
    .isNotEmpty
  result.kspGeneratedSources
} else {
  assertThat(result.sourcesGeneratedByAnnotationProcessor)
    .isNotEmpty
  result.sourcesGeneratedByAnnotationProcessor
}

@gfreivasc Can you explain, where the listFilesRecursively comes from?

I could not elaborate this, so I use walkTopDown extension method method from kotlin.io instead:

val KotlinCompilation.Result.kspGeneratedSources: List<File> get() {
  val kspWorkingDir = workingDir.resolve("ksp")
  val kspGeneratedDir = kspWorkingDir.resolve("sources")
  val kotlinGeneratedDir = kspGeneratedDir.resolve("kotlin")
  val javaGeneratedDir = kspGeneratedDir.resolve("java")
  return kotlinGeneratedDir.walkTopDown().toList() +
    javaGeneratedDir.walkTopDown()
}

@chausknecht I created this method, or got it from somewhere Idk it's been a while. I didn't include it in this excerpt for shortness sake but you can see it in this PR

@gfreivasc Thx for answering :-) After looking at the handcrafted method, I tend to prefer the walkTopDown approach.