Introduce KotlinGradleBuildServices that is created once per build
This commit is contained in:
+40
-26
@@ -19,13 +19,13 @@ package org.jetbrains.kotlin.gradle.plugin
|
||||
import org.apache.commons.lang.SystemUtils
|
||||
import org.gradle.BuildAdapter
|
||||
import org.gradle.BuildResult
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.invocation.Gradle
|
||||
import org.gradle.api.logging.Logging
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.com.intellij.openapi.util.io.ZipFileCache
|
||||
import org.jetbrains.kotlin.com.intellij.openapi.vfs.impl.ZipHandler
|
||||
import org.jetbrains.kotlin.com.intellij.openapi.vfs.impl.jar.CoreJarFileSystem
|
||||
|
||||
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
|
||||
|
||||
private fun comparableVersionStr(version: String) =
|
||||
"(\\d+)\\.(\\d+).*"
|
||||
@@ -37,9 +37,26 @@ private fun comparableVersionStr(version: String) =
|
||||
?.let { if (it.all { (it?.value?.length ?: 0).let { it > 0 && it < 4 }}) it else null }
|
||||
?.joinToString(".", transform = { it!!.value.padStart(3, '0') })
|
||||
|
||||
class CleanUpBuildListener(private val project: Project) : BuildAdapter() {
|
||||
class KotlinGradleBuildServices private constructor(): BuildAdapter() {
|
||||
companion object {
|
||||
const val FORCE_SYSTEM_GC_MESSAGE = "Forcing System.gc()"
|
||||
const val INIT_MESSAGE = "Initialized KotlinGradleBuildServices"
|
||||
const val DISPOSE_MESSAGE = "Disposed KotlinGradleBuildServices"
|
||||
private var initialized = false
|
||||
|
||||
@JvmStatic
|
||||
@Synchronized
|
||||
fun init(gradle: Gradle) {
|
||||
if (initialized) return
|
||||
|
||||
val listener = KotlinGradleBuildServices()
|
||||
gradle.addBuildListener(listener)
|
||||
initialized = true
|
||||
val log = Logging.getLogger(KotlinGradleBuildServices::class.java)
|
||||
log.kotlinDebug(INIT_MESSAGE)
|
||||
|
||||
listener.buildStarted()
|
||||
}
|
||||
}
|
||||
|
||||
private val log = Logging.getLogger(this.javaClass)
|
||||
@@ -54,34 +71,31 @@ class CleanUpBuildListener(private val project: Project) : BuildAdapter() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun buildFinished(result: BuildResult?) {
|
||||
val gradle = result?.gradle
|
||||
if (gradle != null) {
|
||||
override fun buildFinished(result: BuildResult) {
|
||||
val gradle = result.gradle!!
|
||||
val kotlinCompilerCalled = gradle.rootProject.allprojects.flatMap { it.tasks }.any { it is AbstractKotlinCompile<*> && it.compilerCalled }
|
||||
|
||||
val kotlinCompilerCalled = project.tasks.filter { it.name.contains("kotlin", ignoreCase = true) }
|
||||
.any { task -> task.hasProperty("compilerCalled") && task.property("compilerCalled") as? Boolean ?: false }
|
||||
if (kotlinCompilerCalled) {
|
||||
log.kotlinDebug("Cleanup after kotlin")
|
||||
cleanup(gradle.gradleVersion)
|
||||
}
|
||||
else {
|
||||
log.kotlinDebug("Skipping kotlin cleanup since compiler wasn't called")
|
||||
}
|
||||
|
||||
if (kotlinCompilerCalled) {
|
||||
log.kotlinDebug("Cleanup after kotlin")
|
||||
|
||||
cleanup(gradle.gradleVersion)
|
||||
}
|
||||
else {
|
||||
log.kotlinDebug("Skipping kotlin cleanup since compiler wasn't called")
|
||||
}
|
||||
|
||||
gradle.removeListener(this)
|
||||
|
||||
if (kotlinCompilerCalled) {
|
||||
startMemory?.let { startMemoryCopy ->
|
||||
getUsedMemoryKb()?.let { endMemory ->
|
||||
// the value reported here is not necessarily a leak, since it is calculated before collecting the plugin classes
|
||||
// but on subsequent runs in the daemon it should be rather small, then the classes are actually reused by the daemon (see above)
|
||||
log.kotlinDebug("[PERF] Used memory after build: $endMemory kb (difference since build start: ${"%+d".format(endMemory - startMemoryCopy)} kb)")
|
||||
}
|
||||
if (kotlinCompilerCalled) {
|
||||
startMemory?.let { startMemoryCopy ->
|
||||
getUsedMemoryKb()?.let { endMemory ->
|
||||
// the value reported here is not necessarily a leak, since it is calculated before collecting the plugin classes
|
||||
// but on subsequent runs in the daemon it should be rather small, then the classes are actually reused by the daemon (see above)
|
||||
log.kotlinDebug("[PERF] Used memory after build: $endMemory kb (difference since build start: ${"%+d".format(endMemory - startMemoryCopy)} kb)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gradle.removeListener(this)
|
||||
initialized = false
|
||||
log.kotlinDebug(DISPOSE_MESSAGE)
|
||||
}
|
||||
|
||||
private fun getUsedMemoryKb(): Long? {
|
||||
+1
-3
@@ -23,9 +23,7 @@ abstract class KotlinBasePluginWrapper(protected val fileResolver: FileResolver)
|
||||
val plugin = getPlugin()
|
||||
plugin.apply(project)
|
||||
|
||||
val cleanUpBuildListener = CleanUpBuildListener(project)
|
||||
cleanUpBuildListener.buildStarted()
|
||||
project.gradle.addBuildListener(cleanUpBuildListener)
|
||||
KotlinGradleBuildServices.init(project.gradle)
|
||||
}
|
||||
|
||||
protected abstract fun getPlugin(): Plugin<Project>
|
||||
|
||||
+2
@@ -43,6 +43,7 @@ abstract class AbstractKotlinAndroidGradleTests(
|
||||
":compileFlavor1DebugUnitTestKotlin",
|
||||
"InternalDummyTest PASSED",
|
||||
":compileFlavor1DebugAndroidTestKotlin")
|
||||
checkKotlinGradleBuildServices()
|
||||
}
|
||||
|
||||
// Run the build second time, assert everything is up-to-date
|
||||
@@ -69,6 +70,7 @@ abstract class AbstractKotlinAndroidGradleTests(
|
||||
":compileFlavor2Jnidebug",
|
||||
":compileFlavor1Release",
|
||||
":compileFlavor2Release")
|
||||
checkKotlinGradleBuildServices()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+11
@@ -2,6 +2,7 @@ package org.jetbrains.kotlin.gradle
|
||||
|
||||
import org.gradle.api.logging.LogLevel
|
||||
import org.jetbrains.kotlin.com.intellij.openapi.util.io.FileUtil
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinGradleBuildServices
|
||||
import org.jetbrains.kotlin.gradle.util.createGradleCommand
|
||||
import org.jetbrains.kotlin.gradle.util.runProcess
|
||||
import org.junit.After
|
||||
@@ -164,6 +165,16 @@ abstract class BaseGradleIT {
|
||||
return this
|
||||
}
|
||||
|
||||
fun CompiledProject.assertSubstringCount(substring: String, expectedCount: Int) {
|
||||
val actualCount = substring.toRegex().findAll(output).count()
|
||||
assertEquals(expectedCount, actualCount, "Number of occurrences in output for substring '$substring'")
|
||||
}
|
||||
|
||||
fun CompiledProject.checkKotlinGradleBuildServices() {
|
||||
assertSubstringCount(KotlinGradleBuildServices.INIT_MESSAGE, expectedCount = 1)
|
||||
assertSubstringCount(KotlinGradleBuildServices.DISPOSE_MESSAGE, expectedCount = 1)
|
||||
}
|
||||
|
||||
fun CompiledProject.assertNotContains(vararg expected: String): CompiledProject {
|
||||
for (str in expected) {
|
||||
assertFalse(output.contains(str.normalize()), "Output should not contain '$str'")
|
||||
|
||||
+4
-3
@@ -1,7 +1,7 @@
|
||||
package org.jetbrains.kotlin.gradle
|
||||
|
||||
import org.gradle.api.logging.LogLevel
|
||||
import org.jetbrains.kotlin.gradle.plugin.CleanUpBuildListener
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinGradleBuildServices
|
||||
import org.jetbrains.kotlin.gradle.tasks.USING_EXPERIMENTAL_INCREMENTAL_MESSAGE
|
||||
import org.jetbrains.kotlin.gradle.util.getFileByName
|
||||
import org.junit.Test
|
||||
@@ -108,12 +108,12 @@ class KotlinGradleIT: BaseGradleIT() {
|
||||
fun testLogLevelForceGC() {
|
||||
val debugProject = Project("simpleProject", GRADLE_VERSION, minLogLevel = LogLevel.DEBUG)
|
||||
debugProject.build("build") {
|
||||
assertContains(CleanUpBuildListener.FORCE_SYSTEM_GC_MESSAGE)
|
||||
assertContains(KotlinGradleBuildServices.FORCE_SYSTEM_GC_MESSAGE)
|
||||
}
|
||||
|
||||
val infoProject = Project("simpleProject", GRADLE_VERSION, minLogLevel = LogLevel.INFO)
|
||||
infoProject.build("clean", "build") {
|
||||
assertNotContains(CleanUpBuildListener.FORCE_SYSTEM_GC_MESSAGE)
|
||||
assertNotContains(KotlinGradleBuildServices.FORCE_SYSTEM_GC_MESSAGE)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,6 +141,7 @@ class KotlinGradleIT: BaseGradleIT() {
|
||||
assertSuccessful()
|
||||
assertReportExists("subproject")
|
||||
assertContains(":subproject:compileKotlin", ":subproject:compileTestKotlin")
|
||||
checkKotlinGradleBuildServices()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user