[KAPT] Cache annotation processors' classloaders

Adds experimental feature to cache annotation processors' classloaders
It should increase speed of KAPT for consecutive runs
Works for gradle only (it stores cl in gradle daemon)
#KT-28901 Fixed
This commit is contained in:
Andrey Zinovyev
2021-03-02 10:55:42 +03:00
committed by TeamCityServer
parent e6b9935df9
commit 1093bffc62
16 changed files with 419 additions and 41 deletions
@@ -258,7 +258,8 @@ abstract class BaseGradleIT {
val verbose: Boolean,
val useWorkers: Boolean,
val incrementalKapt: Boolean = false,
val includeCompileClasspath: Boolean = true
val includeCompileClasspath: Boolean = true,
val classLoadersCacheSize: Int? = null
)
open inner class Project(
@@ -913,6 +914,9 @@ Finished executing task ':$taskName'|
add("-Pkapt.use.worker.api=${kaptOptions.useWorkers}")
add("-Pkapt.incremental.apt=${kaptOptions.incrementalKapt}")
add("-Pkapt.include.compile.classpath=${kaptOptions.includeCompileClasspath}")
kaptOptions.classLoadersCacheSize?.also { cacheSize ->
add("-Pkapt.classloaders.cache.size=$cacheSize")
}
}
options.parallelTasksInProject?.let {
@@ -21,11 +21,14 @@ import org.jetbrains.kotlin.gradle.tasks.USING_JVM_INCREMENTAL_COMPILATION_MESSA
import org.jetbrains.kotlin.gradle.util.*
import org.junit.Assert
import org.junit.Assume
import org.junit.Ignore
import org.junit.Test
import java.io.File
import java.util.regex.Pattern
import java.util.zip.ZipFile
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.assertTrue
abstract class Kapt3BaseIT : BaseGradleIT() {
companion object {
@@ -43,7 +46,7 @@ abstract class Kapt3BaseIT : BaseGradleIT() {
}
}
class Kapt3WorkersIT : Kapt3IT() {
open class Kapt3WorkersIT : Kapt3IT() {
override fun kaptOptions(): KaptOptions =
super.kaptOptions().copy(useWorkers = true)
@@ -53,7 +56,9 @@ class Kapt3WorkersIT : Kapt3IT() {
Project("javacIsLoadedOnce", directoryPrefix = "kapt2")
project.build("build") {
assertSuccessful()
assertSubstringCount("Loaded com.sun.tools.javac.util.Context from", 1)
val loadsCount = Pattern.quote("Loaded com.sun.tools.javac.util.Context from").toRegex().findAll(output).count()
assertTrue(loadsCount <= 1, "javac is loaded more than once")
}
}
@@ -95,9 +100,65 @@ class Kapt3WorkersIT : Kapt3IT() {
}
}
class Kapt3ClassLoadersCacheIT : Kapt3WorkersIT() {
override fun kaptOptions(): KaptOptions =
super.kaptOptions().copy(classLoadersCacheSize = 10, includeCompileClasspath = false)
@Ignore
override fun testDisableDiscoveryInCompileClasspath() {
//classloaders cache is incompatible with AP discovery in classpath
}
@Test
override fun testAnnotationProcessorAsFqName() {
val project = Project("annotationProcessorAsFqName", directoryPrefix = "kapt2").also { it.setupWorkingDir() }
//classloaders caching is not compatible with includeCompileClasspath
project.projectDir.getFileByName("build.gradle").modify {
it.addBeforeSubstring(
"kapt \"org.jetbrains.kotlin:annotation-processor-example:\$kotlin_version\"\n",
"implementation \"org.jetbrains.kotlin:annotation-processor-example"
)
}
project.build("build") {
assertSuccessful()
assertKaptSuccessful()
assertTasksExecuted(":compileKotlin", ":compileJava")
assertFileExists("build/generated/source/kapt/main/example/TestClassGenerated.java")
assertFileExists(kotlinClassesDir() + "example/TestClass.class")
assertFileExists(javaClassesDir() + "example/TestClassGenerated.class")
}
}
@Test
fun testAnnotationProcessorClassIsLoadedOnce() {
val project = Project("javacIsLoadedOnce", directoryPrefix = "kapt2")
fun CompiledProject.classLoadingCount() =
Pattern.quote("Loaded example.ExampleAnnotationProcessor from").toRegex().findAll(output).count()
project.build("build") {
assertSuccessful()
assertTasksExecuted(":module1:kaptKotlin", ":module2:kaptKotlin")
assertTrue(classLoadingCount() <= 1, "AP class is loaded more than once")
}
project.projectDir.getFilesByNames("Module1Class.kt", "Module2Class.kt").forEach {
it.appendText("\n fun touch() = null")
}
project.build("build") {
assertSuccessful()
assertTasksExecuted(":module1:kaptKotlin", ":module2:kaptKotlin")
assertTrue(classLoadingCount() == 0, "AP class shouldn't be loaded on the second build")
}
}
}
open class Kapt3IT : Kapt3BaseIT() {
@Test
fun testAnnotationProcessorAsFqName() {
open fun testAnnotationProcessorAsFqName() {
val project = Project("annotationProcessorAsFqName", directoryPrefix = "kapt2")
project.build("build") {
@@ -487,7 +548,7 @@ open class Kapt3IT : Kapt3BaseIT() {
}
@Test
fun testDisableDiscoveryInCompileClasspath() = with(Project("kaptAvoidance", directoryPrefix = "kapt2")) {
open fun testDisableDiscoveryInCompileClasspath() = with(Project("kaptAvoidance", directoryPrefix = "kapt2")) {
setupWorkingDir()
val buildGradle = projectDir.resolve("app/build.gradle")
buildGradle.modify {
@@ -510,6 +571,12 @@ open class Kapt3IT : Kapt3BaseIT() {
@Test
fun testKaptAvoidance() = with(Project("kaptAvoidance", directoryPrefix = "kapt2")) {
setupWorkingDir()
projectDir.resolve("app/build.gradle").modify {
"$it\n\nkapt.includeCompileClasspath = true"
}
build("assemble") {
assertSuccessful()
assertTasksExecuted(
@@ -538,7 +605,10 @@ open class Kapt3IT : Kapt3BaseIT() {
// enable discovery
projectDir.resolve("app/build.gradle").modify {
"$it\n\nkapt.includeCompileClasspath = false"
it.replace(
"kapt.includeCompileClasspath = true",
"kapt.includeCompileClasspath = false"
)
}
build("assemble") {
assertSuccessful()
@@ -710,7 +780,7 @@ open class Kapt3IT : Kapt3BaseIT() {
assertSuccessful()
assertKaptSuccessful()
assertTasksExecuted(":compileKotlin", ":compileJava")
}
}
}
@@ -3,9 +3,12 @@ buildscript {
'kotlin': [
'stdlib': [
'common': "org.jetbrains.kotlin:kotlin-stdlib-common:$kotlin_version",
'jdk': "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version",
],
'jdk': "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
]
],
'other' : [
'ap_example': "org.jetbrains.kotlin:annotation-processor-example:$kotlin_version"
]
]
repositories {
@@ -7,4 +7,5 @@ targetCompatibility = JavaVersion.VERSION_1_8
dependencies {
expectedBy project(':dac')
implementation deps.kotlin.stdlib.jdk
kapt deps.other.ap_example
}