[KAPT4] KT-51982 Implement Kapt4AnalysisHandlerExtension, add KAPT CLI and Gradle IT

Co-authored-by: Alexander Udalov <Alexander.Udalov@jetbrains.com>
This commit is contained in:
Alexander Udalov
2023-08-31 22:19:13 +00:00
committed by Space Team
parent d50d36f16c
commit 5c19cb3fcb
61 changed files with 1577 additions and 68 deletions
@@ -36,7 +36,8 @@ import kotlin.io.path.deleteExisting
import kotlin.io.path.outputStream
import kotlin.test.assertEquals
abstract class Kapt3BaseIT(val languageVersion: String = "1.9") : KGPBaseTest() {
abstract class Kapt3BaseIT : KGPBaseTest() {
companion object {
private const val KAPT_SUCCESSFUL_MESSAGE = "Annotation processing complete, errors: 0"
}
@@ -44,8 +45,7 @@ abstract class Kapt3BaseIT(val languageVersion: String = "1.9") : KGPBaseTest()
override val defaultBuildOptions: BuildOptions = super.defaultBuildOptions
.copy(
kaptOptions = this.kaptOptions(),
languageVersion = languageVersion,
)
).copyEnsuringK1()
protected open fun kaptOptions(): BuildOptions.KaptOptions = BuildOptions.KaptOptions(
verbose = true,
@@ -74,8 +74,8 @@ abstract class Kapt3BaseIT(val languageVersion: String = "1.9") : KGPBaseTest()
*
* then override and disable the test here via `@Disabled`.
*/
@DisplayName("Kapt with classloaders cache")
class Kapt3ClassLoadersCacheIT : Kapt3IT() {
@DisplayName("Kapt 3 with classloaders cache")
open class Kapt3ClassLoadersCacheIT : Kapt3IT() {
override fun kaptOptions(): BuildOptions.KaptOptions = super.kaptOptions().copy(
classLoadersCacheSize = 10,
includeCompileClasspath = false
@@ -138,7 +138,7 @@ class Kapt3ClassLoadersCacheIT : Kapt3IT() {
}
}
@DisplayName("Kapt base checks")
@DisplayName("Kapt 3 base checks")
@OtherGradlePluginTests
open class Kapt3IT : Kapt3BaseIT() {
@DisplayName("Kapt is skipped when no annotation processors are added")
@@ -486,7 +486,7 @@ open class Kapt3IT : Kapt3BaseIT() {
@DisplayName("Should incrementally rebuild on classpath change")
@GradleTest
fun testChangeClasspathICRebuild(gradleVersion: GradleVersion) {
open fun testChangeClasspathICRebuild(gradleVersion: GradleVersion) {
testICRebuild(gradleVersion) { project ->
project.buildGradle.modify {
"$it\ndependencies { implementation 'org.jetbrains.kotlin:kotlin-reflect:' + kotlin_version }"
@@ -944,7 +944,7 @@ open class Kapt3IT : Kapt3BaseIT() {
@DisplayName("kapt works with old MPP")
@GradleTest
fun testMPPKaptPresence(gradleVersion: GradleVersion) {
open fun testMPPKaptPresence(gradleVersion: GradleVersion) {
project("mpp-kapt-presence".withPrefix, gradleVersion) {
build("build") {
@@ -1051,7 +1051,7 @@ open class Kapt3IT : Kapt3BaseIT() {
@DisplayName("KT-46651: kapt is tracking source files properly with configuration cache enabled")
@GradleTest
fun kaptGenerateStubsShouldNotCaptureSourcesStateInConfigurationCache(gradleVersion: GradleVersion) {
open fun kaptGenerateStubsShouldNotCaptureSourcesStateInConfigurationCache(gradleVersion: GradleVersion) {
project(
"incrementalRebuild".withPrefix,
gradleVersion,
@@ -1208,7 +1208,7 @@ open class Kapt3IT : Kapt3BaseIT() {
@DisplayName("Kapt runs in fallback mode with useK2 = true")
@GradleTest
internal fun fallBackModeWithUseK2(gradleVersion: GradleVersion) {
open fun fallBackModeWithUseK2(gradleVersion: GradleVersion) {
project("simple".withPrefix, gradleVersion) {
buildGradle.appendText(
"""
@@ -1237,7 +1237,7 @@ open class Kapt3IT : Kapt3BaseIT() {
@DisplayName("Kapt runs in fallback mode with languageVersion = 2.0")
@GradleTest
internal fun fallBackModeWithLanguageVersion2_0(gradleVersion: GradleVersion) {
open fun fallBackModeWithLanguageVersion2_0(gradleVersion: GradleVersion) {
project("simple".withPrefix, gradleVersion) {
buildGradle.appendText(
"""
@@ -1347,4 +1347,19 @@ open class Kapt3IT : Kapt3BaseIT() {
}
}
}
@DisplayName("Application of annotation processors is repeated as long as new source files are generated")
@GradleTest
open fun testMultipleProcessingPasses(gradleVersion: GradleVersion) {
project("multipass".withPrefix, gradleVersion) {
build("build") {
assertKaptSuccessful()
assertOutputContains("No elements for AnnotationProcessor3")
assertOutputContains("No elements for AnnotationProcessor2")
assertFileInProjectExists("example/build/generated/source/kapt/main/generated/TestClass1.java")
assertFileInProjectExists("example/build/generated/source/kapt/main/generated/TestClass12.java")
assertFileInProjectExists("example/build/generated/source/kapt/main/generated/TestClass123.java")
}
}
}
}
@@ -0,0 +1,103 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.gradle
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.gradle.testbase.TestProject
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.DisplayName
import kotlin.io.path.appendText
import kotlin.io.path.name
import kotlin.io.path.walk
@DisplayName("Kapt 4 base checks")
class Kapt4IT : Kapt3IT() {
override val defaultBuildOptions = super.defaultBuildOptions.copyEnsuringK2()
override fun TestProject.customizeProject() {
forceKapt4()
}
@Disabled("Currently failing. See KT-60950")
override fun kaptGenerateStubsShouldNotCaptureSourcesStateInConfigurationCache(gradleVersion: GradleVersion) {}
@Disabled("Currently failing. See KT-60951")
override fun testChangeClasspathICRebuild(gradleVersion: GradleVersion) {}
@Disabled("Doesn't make sense in Kapt 4")
override fun useGeneratedKotlinSourceK2(gradleVersion: GradleVersion) {}
@Disabled("Doesn't make sense in Kapt 4")
override fun fallBackModeWithUseK2(gradleVersion: GradleVersion) {}
@Disabled("Doesn't make sense in Kapt 4")
override fun fallBackModeWithLanguageVersion2_0(gradleVersion: GradleVersion) {}
@Disabled("Doesn't make sense in Kapt 4")
override fun testRepeatableAnnotationsWithOldJvmBackend(gradleVersion: GradleVersion) {}
@Disabled("Doesn't work in 2.0. Neither with Kapt 3 nor with Kapt 4")
override fun testMPPKaptPresence(gradleVersion: GradleVersion) {}
}
@DisplayName("Kapt 4 with classloaders cache")
class Kapt4ClassLoadersCacheIT : Kapt3ClassLoadersCacheIT() {
override val defaultBuildOptions = super.defaultBuildOptions.copyEnsuringK2()
override fun TestProject.customizeProject() {
forceKapt4()
}
@Disabled("Doesn't make sense in Kapt 4")
override fun useGeneratedKotlinSourceK2(gradleVersion: GradleVersion) {}
@Disabled("Doesn't make sense in Kapt 4")
override fun fallBackModeWithUseK2(gradleVersion: GradleVersion) {}
@Disabled("Doesn't make sense in Kapt 4")
override fun fallBackModeWithLanguageVersion2_0(gradleVersion: GradleVersion) {}
@Disabled("Doesn't make sense in Kapt 4")
override fun testRepeatableAnnotationsWithOldJvmBackend(gradleVersion: GradleVersion) {}
@Disabled("Doesn't work in 2.0. Neither with Kapt 3 nor with Kapt 4")
override fun testMPPKaptPresence(gradleVersion: GradleVersion) {}
@Disabled("Currently failing. See KT-60950")
override fun kaptGenerateStubsShouldNotCaptureSourcesStateInConfigurationCache(gradleVersion: GradleVersion) {}
@Disabled("Currently failing. See KT-60951")
override fun testChangeClasspathICRebuild(gradleVersion: GradleVersion) {}
}
fun TestProject.forceKapt4() {
projectPath.walk().forEach {
when (it.fileName.name) {
"build.gradle" -> it.appendText(
"""
pluginManager.withPlugin('kotlin') {
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
compilerOptions.freeCompilerArgs.addAll(['-Xuse-kapt4', '-Xsuppress-version-warnings'])
}
}
""".trimIndent()
)
"build.gradle.kts" -> it.appendText(
"""
pluginManager.withPlugin("kotlin") {
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile::class.java).configureEach {
compilerOptions.freeCompilerArgs.addAll(listOf("-Xuse-kapt4", "-Xsuppress-version-warnings"))
}
}
""".trimIndent()
)
}
}
}
@@ -15,7 +15,7 @@ import kotlin.io.path.appendText
@DisplayName("android with kapt3 external dependencies tests")
@AndroidGradlePluginTests
class Kapt3AndroidExternalIT : Kapt3BaseIT() {
open class Kapt3AndroidExternalIT : Kapt3BaseIT() {
// Deprecated and doesn't work with Gradle 8 + AGP 8, so keeping max Gradle version as 7.6
// For example: https://github.com/JakeWharton/butterknife/issues/1686
@@ -15,7 +15,7 @@ import kotlin.io.path.writeText
@DisplayName("android with kapt3 tests")
@AndroidGradlePluginTests
class Kapt3AndroidIT : Kapt3BaseIT() {
open class Kapt3AndroidIT : Kapt3BaseIT() {
@DisplayName("KT-15001")
@GradleAndroidTest
fun testKt15001(
@@ -78,7 +78,7 @@ open class Kapt3AndroidIncrementalIT : Kapt3BaseIT() {
@DisplayName("incremental compilation works with dagger")
@GradleAndroidTest
fun testAndroidDaggerIC(
open fun testAndroidDaggerIC(
gradleVersion: GradleVersion,
agpVersion: String,
jdkVersion: JdkVersions.ProvidedJdk,
@@ -205,6 +205,6 @@ open class Kapt3AndroidIncrementalIT : Kapt3BaseIT() {
}
@DisplayName("android with kapt3 incremental build tests with precise compilation outputs backup")
class Kapt3AndroidIncrementalWithPreciseBackupIT : Kapt3AndroidIncrementalIT() {
open class Kapt3AndroidIncrementalWithPreciseBackupIT : Kapt3AndroidIncrementalIT() {
override val defaultBuildOptions = super.defaultBuildOptions.copy(usePreciseOutputsBackup = true, keepIncrementalCompilationCachesInMemory = true)
}
@@ -0,0 +1,20 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.gradle.android
import org.jetbrains.kotlin.config.LanguageVersion
import org.jetbrains.kotlin.gradle.forceKapt4
import org.jetbrains.kotlin.gradle.testbase.TestProject
import org.junit.jupiter.api.DisplayName
@DisplayName("android with kapt4 external dependencies tests")
class Kapt4AndroidExternalIT : Kapt3AndroidExternalIT() {
override val defaultBuildOptions = super.defaultBuildOptions.copyEnsuringK2()
override fun TestProject.customizeProject() {
forceKapt4()
}
}
@@ -0,0 +1,19 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.gradle.android
import org.jetbrains.kotlin.gradle.forceKapt4
import org.jetbrains.kotlin.gradle.testbase.TestProject
import org.junit.jupiter.api.DisplayName
@DisplayName("android with kapt4 tests")
class Kapt4AndroidIT : Kapt3AndroidIT() {
override val defaultBuildOptions = super.defaultBuildOptions.copyEnsuringK2()
override fun TestProject.customizeProject() {
forceKapt4()
}
}
@@ -0,0 +1,37 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.gradle.android
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.gradle.forceKapt4
import org.jetbrains.kotlin.gradle.testbase.JdkVersions
import org.jetbrains.kotlin.gradle.testbase.TestProject
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.DisplayName
@DisplayName("android with kapt4 incremental build tests")
class Kapt4AndroidIncrementalIT : Kapt3AndroidIncrementalIT() {
override val defaultBuildOptions = super.defaultBuildOptions.copyEnsuringK2()
override fun TestProject.customizeProject() {
forceKapt4()
}
@Disabled("See KT-61628")
override fun testAndroidDaggerIC(gradleVersion: GradleVersion, agpVersion: String, jdkVersion: JdkVersions.ProvidedJdk) {}
}
@DisplayName("android with kapt4 incremental build tests with precise compilation outputs backup")
class Kapt4AndroidIncrementalWithPreciseBackupIT : Kapt3AndroidIncrementalWithPreciseBackupIT() {
override val defaultBuildOptions = super.defaultBuildOptions.copyEnsuringK2()
override fun TestProject.customizeProject() {
forceKapt4()
}
@Disabled("See KT-61628")
override fun testAndroidDaggerIC(gradleVersion: GradleVersion, agpVersion: String, jdkVersion: JdkVersions.ProvidedJdk) {}
}
@@ -35,4 +35,6 @@ abstract class KGPBaseTest {
@TempDir
lateinit var workingDir: Path
internal open fun TestProject.customizeProject() {}
}
@@ -82,6 +82,8 @@ fun KGPBaseTest.project(
localRepoDir?.let { testProject.configureLocalRepository(localRepoDir) }
if (buildJdk != null) testProject.setupNonDefaultJdk(buildJdk)
testProject.customizeProject()
val result = runCatching {
testProject.test()
}
@@ -0,0 +1,7 @@
plugins {
id 'org.jetbrains.kotlin.jvm'
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
}
@@ -0,0 +1,77 @@
package processors
import javax.annotation.processing.*
import javax.lang.model.SourceVersion
import javax.lang.model.element.TypeElement
import javax.tools.Diagnostic
annotation class Annotation1
annotation class Annotation2
annotation class Annotation3
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes("processors.Annotation1")
class AnnotationProcessor1 : AbstractProcessor() {
override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {
val elements = roundEnv.getElementsAnnotatedWith(Annotation1::class.java)
if (elements.isEmpty()) {
processingEnv.messager.printMessage(Diagnostic.Kind.NOTE, "No elements for ${this::class.java.simpleName}")
}
for (element in elements) {
val generatedSimpleName = "${element.simpleName}1"
val file = processingEnv.filer.createSourceFile("generated.$generatedSimpleName")
file.openWriter().use {
it.write("package generated;\n@processors.Annotation2\npublic class $generatedSimpleName {}")
}
}
return true
}
}
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes("processors.Annotation2")
class AnnotationProcessor2 : AbstractProcessor() {
override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {
val elements = roundEnv.getElementsAnnotatedWith(Annotation2::class.java)
if (elements.isEmpty()) {
processingEnv.messager.printMessage(Diagnostic.Kind.NOTE, "No elements for ${this::class.java.simpleName}")
}
for (element in elements) {
val generatedSimpleName = "${element.simpleName}2"
val file = processingEnv.filer.createSourceFile("generated.$generatedSimpleName")
file.openWriter().use {
it.write("package generated;\n@processors.Annotation3\npublic class $generatedSimpleName {}")
}
}
return true
}
}
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes("processors.Annotation3")
class AnnotationProcessor3 : AbstractProcessor() {
override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {
val elements = roundEnv.getElementsAnnotatedWith(Annotation3::class.java)
if (elements.isEmpty()) {
processingEnv.messager.printMessage(Diagnostic.Kind.NOTE, "No elements for ${this::class.java.simpleName}")
}
for (element in elements) {
val generatedSimpleName = "${element.simpleName}3"
val file = processingEnv.filer.createSourceFile("generated.$generatedSimpleName")
file.openWriter().use {
it.write("package generated;\npublic class $generatedSimpleName {}")
}
}
return true
}
}
@@ -0,0 +1,6 @@
allprojects {
repositories {
mavenLocal()
mavenCentral()
}
}
@@ -0,0 +1,26 @@
plugins {
id 'org.jetbrains.kotlin.jvm'
id 'org.jetbrains.kotlin.kapt'
id 'idea'
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation project(":annotation-processors")
kapt project(":annotation-processors")
testImplementation'junit:junit:4.13.2'
}
idea {
module {
sourceDirs += files('build/generated/source/kapt/main', 'build/generated/source/kaptKotlin/main')
generatedSourceDirs += files('build/generated/source/kapt/main', 'build/generated/source/kaptKotlin/main')
}
}
kapt {
// The "reverse" order requires three passes
annotationProcessors("processors.AnnotationProcessor3", "processors.AnnotationProcessor2", "processors.AnnotationProcessor1")
}
@@ -0,0 +1,6 @@
package example
import processors.Annotation1
@Annotation1
class TestClass
@@ -0,0 +1,11 @@
package example
import org.junit.Test
import org.junit.Assert.*
import generated.TestClass123
class AnnotationTest {
@Test fun testSimple() {
assertEquals("TestClass123", TestClass123::class.java.simpleName)
}
}
@@ -0,0 +1 @@
include ':annotation-processors', ':example'