4

I'm developing an KSP annotation processor for Kotlin. The code is generated correctly during the compile time and I can see the generated classes in the output directory. Now I want to test my annotation processor via JUnit and "com.github.tschuchortdev.KotlinCompilation". If I call the compile method the code will be generated and I can see the generated class in Temp-Directory but if I try to load the class then I get a "java.lang.ClassNotFoundException: test.pack.TestClassDslBuilder" exception. I hope the code is self-explanatory. My question is: Why isn't the classes compiled and not loadable? Maybe there's a missing configuration of the kompiler.

    @BeforeEach
    fun setup() {
        val kotlinSource = SourceFile.kotlin(
            "TestClass.kt", """
                package test.pack

                import yy.xxx.dsl.builder.annotation.DslBuilder
                @DslBuilder        
                class TestClass {

                }
            """
        )

        val compilation = KotlinCompilation().apply {
            sources = listOf(kotlinSource)
            symbolProcessorProviders = listOf(DslBuilderProcessorProvider())
            //workingDir =
            inheritClassPath = true
            verbose = false
            //messageOutputStream = System.out
            kspIncremental = true
        }
        compilationResult = compilation.compile()
        assertEquals(KotlinCompilation.ExitCode.OK, compilationResult.exitCode)

        // The next line leads to java.lang.ClassNotFoundException 
        compilationResult.classLoader.loadClass("test.pack.TestClassDslBuilder")
    }
Jayser
  • 371
  • 4
  • 15
  • Maybe it isn't possible to load a class at this time. But it's working with an kapt annotation processor. – Jayser Jan 04 '22 at 15:52
  • I don't know if it will help, but this article https://proandroiddev.com/ksp-fact-or-kapt-7c7e9218c575 has some tips for testing ksp. It includes a link to a compiler testing tool that has ksp test tools https://github.com/tschuchortdev/kotlin-compile-testing. – Mike Two Jan 15 '22 at 20:17
  • Is the file generated? You need to assert that the file is present, then assert the content of it or class loading being able to load the class. – Nikola Despotoski Jun 15 '22 at 10:21

2 Answers2

0

I have the same issue. Didn't fix it, but it seems to me, that problem is in path of generated file (which you wrote like string), like in google compile testing it was important to use StandardLocation.SOURCE_OUTPUT, when you try to find generatedFile(). But in my case, it doesn't generate anything - I mean generatedFiles.size() == 0 in KotlinCompilation.Result

  • 2
    As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jun 23 '22 at 06:39
0

This lib is not compatible with KSP fully yet. There is a hack to make it work with double compilation: https://github.com/tschuchortdev/kotlin-compile-testing/issues/72#issuecomment-744475289

fun compile(tempDir: File, compilation: KotlinCompilation): KotlinCompilation.Result {
    val pass1 = compilation.compile()
    require(pass1.exitCode == KotlinCompilation.ExitCode.OK) {
        "Cannot do the 1st pass \n ${pass1.messages}"
    }
    val pass2 = KotlinCompilation().apply {
        sources = compilation.kspGeneratedSourceFiles(tempDir) + compilation.sources
        inheritClassPath = true
    }.compile()
    require(pass2.exitCode == KotlinCompilation.ExitCode.OK) {
        "Cannot do the 2nd pass \n ${pass2.messages}"
    }
    return pass2
}

private fun KotlinCompilation.kspGeneratedSourceFiles(tempDir: File): List<SourceFile> =
    kspGeneratedSources(tempDir)
        .filter { it.isFile }
        .map { SourceFile.fromPath(it.absoluteFile) }
        .toList()
}

private fun kspGeneratedSources(tempDir: File): List<File> {
    val kspWorkingDir = tempDir.resolve("ksp")
    val kspGeneratedDir = kspWorkingDir.resolve("sources")
    val kotlinGeneratedDir = kspGeneratedDir.resolve("kotlin")
    val javaGeneratedDir = kspGeneratedDir.resolve("java")
    return kotlinGeneratedDir.walkTopDown().toList() +
            javaGeneratedDir.walkTopDown()
}

And I can confirm that it works. This is just an example. Depending on your output directory, you might want to tinker with the file paths a bit, of course. If it still doesn't work, make sure that the second KotlinCompilation() is right for your case.

Michał Klimczak
  • 12,674
  • 8
  • 66
  • 99