From eea01f468b4103e5e96c4c42fd3abd4784dc9d9a Mon Sep 17 00:00:00 2001 From: Alexey Tsvetkov Date: Tue, 16 Aug 2016 19:50:20 +0300 Subject: [PATCH] Introduce KotlinGradleBuildServices that is created once per build --- ...stener.kt => KotlinGradleBuildServices.kt} | 66 +++++++++++-------- .../gradle/plugin/KotlinPluginWrapper.kt | 4 +- .../AbstractKotlinAndroidGradleTests.kt | 2 + .../jetbrains/kotlin/gradle/BaseGradleIT.kt | 11 ++++ .../kotlin/gradle/KotlinGradlePluginIT.kt | 7 +- 5 files changed, 58 insertions(+), 32 deletions(-) rename libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/{CleanUpBuildListener.kt => KotlinGradleBuildServices.kt} (71%) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/CleanUpBuildListener.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleBuildServices.kt similarity index 71% rename from libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/CleanUpBuildListener.kt rename to libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleBuildServices.kt index 6cff7d056a0..b6845f403db 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/CleanUpBuildListener.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleBuildServices.kt @@ -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? { diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt index 54b08ff6caa..ce28927b44f 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt @@ -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 diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/AbstractKotlinAndroidGradleTests.kt b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/AbstractKotlinAndroidGradleTests.kt index 7e054734826..b2702a3b05c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/AbstractKotlinAndroidGradleTests.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/AbstractKotlinAndroidGradleTests.kt @@ -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() } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/BaseGradleIT.kt b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/BaseGradleIT.kt index 00bf19f3f0c..31643b5abec 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/BaseGradleIT.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/BaseGradleIT.kt @@ -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'") diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinGradlePluginIT.kt b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinGradlePluginIT.kt index 3d1dbab2fd7..2faab590b55 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinGradlePluginIT.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinGradlePluginIT.kt @@ -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() } }