KAPT: pass KAPT generated classes to kotlinc classpath

Make sure to pass .class files generated by annotation processors
to KotlinCompile task. This way, Kotlin files can refer to symbols
generated in these .class files during annotation processing.

Disable the test in Kapt3ClassLoadersCacheIT

#KT-33847 Fixed
This commit is contained in:
Nicklas Ansman
2022-10-11 19:20:20 -04:00
committed by nataliya.valtman
parent 80fa765333
commit 1417c8961b
15 changed files with 183 additions and 6 deletions
@@ -90,6 +90,10 @@ class Kapt3ClassLoadersCacheIT : Kapt3IT() {
override fun testChangesToKaptConfigurationDoNotTriggerStubGeneration(gradleVersion: GradleVersion) {
}
@Disabled("classloaders cache is leaking file descriptors that prevents cleaning test project")
override fun testKt33847(gradleVersion: GradleVersion) {
}
override fun testAnnotationProcessorAsFqName(gradleVersion: GradleVersion) {
project("annotationProcessorAsFqName".withPrefix, gradleVersion) {
//classloaders caching is not compatible with includeCompileClasspath
@@ -812,6 +816,38 @@ open class Kapt3IT : Kapt3BaseIT() {
}
}
@DisplayName("KT33847: Kapt does not included Filer-generated class files on compilation classpath")
@GradleTest
open fun testKt33847(gradleVersion: GradleVersion) {
project("kt33847".withPrefix, gradleVersion) {
build("build") {
val processorSubproject = subProject("processor")
processorSubproject
.assertFileInProjectExists("build/tmp/kapt3/classes/main/META-INF/services/javax.annotation.processing.Processor")
val processorJar = processorSubproject.projectPath.resolve("build/libs/processor.jar")
assertFileExists(processorJar)
ZipFile(processorJar.toFile()).use { zip ->
assert(zip.getEntry("META-INF/services/javax.annotation.processing.Processor") != null) {
"Generated annotation processor jar file does not contain processor service entry!"
}
}
assertTasksExecuted(
":api:compileKotlin",
":processor:compileKotlin",
":library:kaptGenerateStubsKotlin",
":library:kaptKotlin",
":library:compileKotlin",
":app:compileKotlin",
)
assertKaptSuccessful()
}
}
}
@DisplayName("Dependency on kapt module should not resolve all configurations")
@GradleTest
fun testDependencyOnKaptModule(gradleVersion: GradleVersion) {
@@ -0,0 +1,7 @@
plugins {
id 'org.jetbrains.kotlin.jvm'
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
}
@@ -0,0 +1,6 @@
package com.example
import kotlin.annotation.AnnotationTarget.CLASS
@Target(CLASS)
annotation class SomeAnnotation
@@ -0,0 +1,8 @@
plugins {
id 'org.jetbrains.kotlin.jvm'
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation project(':library')
}
@@ -0,0 +1,6 @@
package com.example
fun app() {
GeneratedSomeClass
println(StringFactory.generateString())
}
@@ -0,0 +1,10 @@
subprojects {
repositories {
mavenLocal()
mavenCentral()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
@@ -0,0 +1,10 @@
plugins {
id 'org.jetbrains.kotlin.jvm'
id 'org.jetbrains.kotlin.kapt'
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation project(':api')
kapt project(':processor')
}
@@ -0,0 +1,13 @@
package com.example
@SomeAnnotation
object SomeClass {
init {
println(StringFactory.generateString())
}
}
fun library() {
GeneratedSomeClass
println(StringFactory.generateString())
}
@@ -0,0 +1,11 @@
plugins {
id 'org.jetbrains.kotlin.jvm'
id 'org.jetbrains.kotlin.kapt'
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'com.google.auto.service:auto-service:1.0-rc3'
kapt 'com.google.auto.service:auto-service:1.0-rc3'
implementation project(':api')
}
@@ -0,0 +1,58 @@
package com.example
import com.google.auto.service.AutoService
import org.intellij.lang.annotations.Language
import javax.annotation.processing.AbstractProcessor
import javax.annotation.processing.ProcessingEnvironment
import javax.annotation.processing.Processor
import javax.annotation.processing.RoundEnvironment
import javax.lang.model.element.TypeElement
import javax.tools.Diagnostic
import javax.tools.Diagnostic.Kind.MANDATORY_WARNING
import javax.tools.StandardLocation
@AutoService(Processor::class)
class Processor : AbstractProcessor() {
override fun getSupportedAnnotationTypes() = setOf(SomeAnnotation::class.java.canonicalName)
override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {
if (annotations.isNotEmpty()) {
processingEnv.messager.printMessage(Diagnostic.Kind.NOTE, "Writing StringFactory")
processingEnv.filer.createClassFile("com.example.StringFactory", *annotations.toTypedArray())
.openOutputStream()
.use { output ->
javaClass.classLoader.getResourceAsStream("StringFactory.class")!!
.use { it.copyTo(output) }
}
}
for (element in roundEnv.getElementsAnnotatedWith(SomeAnnotation::class.java)) {
val packageName = processingEnv.elementUtils.getPackageOf(element).qualifiedName
val name = "Generated${element.simpleName}"
val file = processingEnv.filer.createResource(
StandardLocation.SOURCE_OUTPUT,
packageName,
"$name.kt",
element
)
processingEnv.messager.printMessage(Diagnostic.Kind.NOTE, "Writing $name")
file.openWriter().use { writer ->
writer.write(
//language=kotlin
"""
package $packageName
object $name {
init {
println(StringFactory.generateString())
}
}
""".trimIndent()
)
}
}
return true
}
}
@@ -0,0 +1,5 @@
rootProject.name = 'kapt3-issues'
include ':api'
include ':processor'
include ':library'
include ':app'
@@ -384,8 +384,11 @@ class Kapt3GradleSubplugin @Inject internal constructor(private val registry: To
kotlinCompilation.output.classesDirs.from(taskProvider.flatMap { it.classesDir })
kotlinCompilation.compileKotlinTaskProvider.configure {
(it as AbstractKotlinCompileTool<*>).setSource(sourcesOutputDir, kotlinSourcesOutputDir)
kotlinCompilation.compileTaskProvider.configure { task ->
with(task as AbstractKotlinCompile<*>) {
setSource(sourcesOutputDir, kotlinSourcesOutputDir)
libraries.from(classesOutputDir)
}
}
}
taskConfigAction.configureTask { task ->
@@ -424,7 +427,7 @@ class Kapt3GradleSubplugin @Inject internal constructor(private val registry: To
val kaptTaskName = getKaptTaskName("kaptGenerateStubs")
val kaptTaskProvider = project.registerTask<KaptGenerateStubsTask>(kaptTaskName)
val taskConfig = KaptGenerateStubsConfig(kotlinCompilation, kotlinCompile)
val taskConfig = KaptGenerateStubsConfig(kotlinCompilation, kotlinCompile, classesOutputDir)
taskConfig.configureTask {
it.stubsDir.set(getKaptStubsDir())
it.destinationDirectory.set(getKaptIncrementalDataDir())
@@ -63,7 +63,7 @@ internal open class KaptConfig<TASK : KaptTask>(
internal constructor(kotlinCompileTask: KotlinCompile, ext: KaptExtension) : this(kotlinCompileTask.project, ext) {
configureTask { task ->
task.classpath.from(kotlinCompileTask.libraries)
task.classpath.from(kotlinCompileTask.libraries - project.files(task.classesDir))
task.compiledSources.from(
kotlinCompileTask.destinationDirectory,
Callable { kotlinCompileTask.javaOutputDir.takeIf { it.isPresent } })
@@ -31,13 +31,17 @@ import java.util.concurrent.ConcurrentHashMap
internal class KaptGenerateStubsConfig : BaseKotlinCompileConfig<KaptGenerateStubsTask> {
constructor(compilation: KotlinCompilationData<*>, kotlinTaskProvider: TaskProvider<KotlinCompile>) : super(compilation) {
constructor(
compilation: KotlinCompilationData<*>,
kotlinTaskProvider: TaskProvider<KotlinCompile>,
kaptClassesDir: File,
) : super(compilation) {
configureFromExtension(project.extensions.getByType(KaptExtension::class.java))
configureTask { task ->
val kotlinCompileTask = kotlinTaskProvider.get()
task.useModuleDetection.value(kotlinCompileTask.useModuleDetection).disallowChanges()
task.moduleName.value(kotlinCompileTask.moduleName).disallowChanges()
task.libraries.from({ kotlinCompileTask.libraries })
task.libraries.from({ kotlinCompileTask.libraries - project.files(kaptClassesDir) })
task.compileKotlinArgumentsContributor.set(providers.provider { kotlinCompileTask.compilerArgumentsContributor })
task.pluginOptions.addAll(kotlinCompileTask.pluginOptions)
// KotlinCompile will also have as input output from KaptGenerateStubTask and KaptTask