From a766369e7283d7b89a5ca2960460dc20365f919a Mon Sep 17 00:00:00 2001 From: Nataliya Valtman Date: Wed, 30 Oct 2019 13:22:59 +0300 Subject: [PATCH] KT-33908 Make Kotlin Gradle plugin compatible with configuration cache #KT-33908 Fixed --- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../AbstractKotlinAndroidGradleTests.kt | 2 +- .../jetbrains/kotlin/gradle/BaseGradleIT.kt | 18 +- .../gradle/ConfigurationCacheForAndroidIT.kt | 73 ++++++ .../kotlin/gradle/ConfigurationCacheIT.kt | 118 ++++++++++ .../kotlin/gradle/InstantExecutionIT.kt | 149 ------------ .../kotlin/gradle/KotlinGradlePluginIT.kt | 4 +- .../org/jetbrains/kotlin/gradle/UpToDateIT.kt | 4 +- .../kotlin/gradle/util/AGPVersion.kt | 3 +- .../testProject/instantExecution/build.gradle | 16 ++ .../instantExecution/lib-project/build.gradle | 5 + .../src/main/kotlin/libproject/Util.kt | 10 + .../main-project/src/build.gradle | 6 + .../main-project/src/main/kotlin/Main.kt | 10 + .../instantExecution/settings.gradle | 1 + .../instantExecutionToJs/build.gradle | 37 +++ .../src/main/kotlin/project/Main.kt | 10 + .../src/test/kotlin/MainTest.kt | 13 ++ .../kt-35942-android/lib1/build.gradle | 8 +- .../kt-35942-android/lib2/build.gradle | 8 +- .../GradleCompilerRunnerWithWorkers.kt | 9 +- .../GradleKotlinCompilerRunner.kt | 59 +++-- .../GradleKotlinCompilerWork.kt | 12 +- .../internal/CompilerArgumentsContributor.kt | 53 ++--- .../kapt/Kapt3KotlinGradleSubplugin.kt | 12 +- .../internal/kapt/KaptGenerateStubsTask.kt | 9 +- .../kotlin/gradle/internal/kapt/KaptTask.kt | 40 +++- .../internal/kapt/KaptWithKotlincTask.kt | 11 +- .../internal/kapt/KaptWithoutKotlincTask.kt | 37 ++- .../model/builder/KotlinModelBuilder.kt | 2 +- .../BuildEventsListenerRegistryHolder.kt | 23 ++ .../plugin/KotlinGradleBuildListener.kt | 21 ++ .../plugin/KotlinGradleBuildServices.kt | 107 ++++----- .../plugin/KotlinGradleFinishBuildHandler.kt | 81 +++++++ .../plugin/KotlinMultiplatformPlugin.kt | 1 + .../kotlin/gradle/plugin/KotlinPlugin.kt | 2 +- .../gradle/plugin/KotlinPluginWrapper.kt | 8 +- .../kotlin/gradle/plugin/gradleUtils.kt | 6 +- .../mpp/GranularMetadataTransformation.kt | 156 +++++++++---- .../mpp/TransformKotlinGranularMetadata.kt | 64 ++++-- .../sources/DefaultLanguageSettingsBuilder.kt | 5 +- .../statistics/KotlinBuildStatHandler.kt | 215 +++++++++++++++++ .../statistics/KotlinBuildStatListener.kt | 66 ++++++ .../statistics/KotlinBuildStatsService.kt | 192 ++-------------- .../gradle/report/KotlinBuildReporter.kt | 99 +------- .../report/KotlinBuildReporterHandler.kt | 110 +++++++++ .../report/KotlinBuildReporterListener.kt | 34 +++ .../js/ir/KotlinJsIrLinkWithWorkers.kt | 3 +- .../targets/jvm/KotlinWithJavaTarget.kt | 9 +- .../KotlinMetadataTargetConfigurator.kt | 25 ++ .../targets/native/KotlinNativeCompilation.kt | 2 - .../native/KotlinNativeTargetConfigurator.kt | 2 +- .../native/KotlinNativeTargetPreset.kt | 9 +- .../native/cocoapods/KotlinCocoapodsPlugin.kt | 7 +- .../performance/KotlinPerformancePlugin.kt | 31 ++- .../tasks/KotlinNativePerformanceTask.kt | 6 +- .../targets/native/tasks/KotlinNativeTasks.kt | 152 ++++++------ .../tasks/InspectClassesForMultiModuleIC.kt | 30 ++- .../gradle/tasks/KotlinCompileCommon.kt | 2 +- .../jetbrains/kotlin/gradle/tasks/Tasks.kt | 217 +++++++++++++----- .../gradle/utils/ParsedGradleVersion.kt | 11 +- .../kotlin/gradle/utils/providerApiUtils.kt | 16 +- 62 files changed, 1629 insertions(+), 824 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/ConfigurationCacheForAndroidIT.kt create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/ConfigurationCacheIT.kt delete mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/InstantExecutionIT.kt create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/instantExecution/build.gradle create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/instantExecution/lib-project/build.gradle create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/instantExecution/lib-project/src/main/kotlin/libproject/Util.kt create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/instantExecution/main-project/src/build.gradle create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/instantExecution/main-project/src/main/kotlin/Main.kt create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/instantExecution/settings.gradle create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/instantExecutionToJs/build.gradle create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/instantExecutionToJs/src/main/kotlin/project/Main.kt create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/instantExecutionToJs/src/test/kotlin/MainTest.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/BuildEventsListenerRegistryHolder.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleBuildListener.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleFinishBuildHandler.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/KotlinBuildStatHandler.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/KotlinBuildStatListener.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/report/KotlinBuildReporterHandler.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/report/KotlinBuildReporterListener.kt diff --git a/idea/testData/perfTest/native/_common/gradle/wrapper/gradle-wrapper.properties b/idea/testData/perfTest/native/_common/gradle/wrapper/gradle-wrapper.properties index 94920145f34..c0494908860 100644 --- a/idea/testData/perfTest/native/_common/gradle/wrapper/gradle-wrapper.properties +++ b/idea/testData/perfTest/native/_common/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.6-20200603220033+0000-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/AbstractKotlinAndroidGradleTests.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/AbstractKotlinAndroidGradleTests.kt index 65bdf322587..6a399afbb98 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/AbstractKotlinAndroidGradleTests.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/AbstractKotlinAndroidGradleTests.kt @@ -726,7 +726,7 @@ fun getSomething() = 10 project.build("assemble", options = options) { assertFailed() - assertContains("Class 'User' is not abstract and does not implement abstract member public abstract fun writeToParcel") + assertContainsRegex("Class 'User' is not abstract and does not implement abstract member public abstract fun (writeToParcel|describeContents)".toRegex()) } File(project.projectDir, "app/build.gradle").modify { it.replace("[\"views\"]", "[\"parcelize\", \"views\"]") } diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BaseGradleIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BaseGradleIT.kt index c5669979e02..f3f506232de 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BaseGradleIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BaseGradleIT.kt @@ -13,7 +13,6 @@ import org.jetbrains.kotlin.gradle.model.ModelContainer import org.jetbrains.kotlin.gradle.model.ModelFetcherBuildAction import org.jetbrains.kotlin.gradle.plugin.KotlinJsCompilerType import org.jetbrains.kotlin.gradle.util.* -import org.jetbrains.kotlin.test.util.trimTrailingWhitespaces import org.junit.After import org.junit.AfterClass import org.junit.Assert @@ -22,6 +21,8 @@ import java.io.File import java.util.regex.Pattern import kotlin.test.* +import org.jetbrains.kotlin.test.util.trimTrailingWhitespaces + val SYSTEM_LINE_SEPARATOR: String = System.getProperty("line.separator") abstract class BaseGradleIT { @@ -205,9 +206,15 @@ abstract class BaseGradleIT { val withBuildCache: Boolean = false, val kaptOptions: KaptOptions? = null, val parallelTasksInProject: Boolean? = null, - val jsCompilerType: KotlinJsCompilerType? = null + val jsCompilerType: KotlinJsCompilerType? = null, + val configurationCache: Boolean = false, + val configurationCacheProblems: ConfigurationCacheProblems = ConfigurationCacheProblems.FAIL ) + enum class ConfigurationCacheProblems { + FAIL, WARN + } + data class KaptOptions( val verbose: Boolean, val useWorkers: Boolean, @@ -707,9 +714,9 @@ Finished executing task ':$taskName'| val xmlString = buildString { appendln("") appendln("") - files.forEach { + files.forEach { file -> appendln( - it.readText() + file.readText() .trimTrailingWhitespaces() .replace(projectDir.absolutePath, "/\$PROJECT_DIR$") .replace(projectDir.name, "\$PROJECT_NAME$") @@ -801,6 +808,9 @@ Finished executing task ':$taskName'| add("-Pkotlin.js.compiler=$it") } + add("-Dorg.gradle.unsafe.configuration-cache=${options.configurationCache}") + add("-Dorg.gradle.unsafe.configuration-cache-problems=${options.configurationCacheProblems.name.toLowerCase()}") + // Workaround: override a console type set in the user machine gradle.properties (since Gradle 4.3): add("--console=plain") addAll(options.freeCommandLineArgs) diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/ConfigurationCacheForAndroidIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/ConfigurationCacheForAndroidIT.kt new file mode 100644 index 00000000000..b01bbe5b8ff --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/ConfigurationCacheForAndroidIT.kt @@ -0,0 +1,73 @@ +/* + * Copyright 2010-2020 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.jetbrains.kotlin.gradle.util.AGPVersion +import org.jetbrains.kotlin.test.KotlinTestUtils +import org.junit.Test + +class ConfigurationCacheForAndroidIT : AbstractConfigurationCacheIT() { + private val androidGradlePluginVersion: AGPVersion + get() = AGPVersion.v4_2_0 + + override fun defaultBuildOptions() = + super.defaultBuildOptions().copy( + androidHome = KotlinTestUtils.findAndroidSdk(), + androidGradlePluginVersion = androidGradlePluginVersion, + configurationCache = true, + /* AGP causes a configuration cache problem: + - plugin 'com.android.internal.application': registration of listener on 'TaskExecutionGraph.addTaskExecutionListener' is unsupported + + which causes tests to fail when configuration-cache-problems=fail is used. + However, everything works fine with WARN level reporting. + TODO: switch to FAIL when AGP no longer causes the cache problem + */ + configurationCacheProblems = ConfigurationCacheProblems.WARN + ) + + @Test + fun testAndroidKaptProject() = with(Project("android-dagger", directoryPrefix = "kapt2")) { + applyAndroid40Alpha4KotlinVersionWorkaround() + projectDir.resolve("gradle.properties").appendText("\nkapt.incremental.apt=false") + testConfigurationCacheOf(":app:compileDebugKotlin", ":app:kaptDebugKotlin", ":app:kaptGenerateStubsDebugKotlin") + } + + @Test + fun testKotlinAndroidProject() = with(Project("AndroidProject")) { + applyAndroid40Alpha4KotlinVersionWorkaround() + testConfigurationCacheOf(":Lib:compileFlavor1DebugKotlin", ":Android:compileFlavor1DebugKotlin") + } + + /** + * Android Gradle plugin 4.0-alpha4 depends on the EAP versions of some o.j.k modules. + * Force the current Kotlin version, so the EAP versions are not queried from the + * test project's repositories, where there's no 'kotlin-eap' repo. + * TODO remove this workaround once an Android Gradle plugin version is used that depends on the stable Kotlin version + */ + private fun Project.applyAndroid40Alpha4KotlinVersionWorkaround() { + setupWorkingDir() + + val resolutionStrategyHack = """ + configurations.all { + resolutionStrategy.dependencySubstitution.all { dependency -> + def requested = dependency.requested + if (requested instanceof ModuleComponentSelector && requested.group == 'org.jetbrains.kotlin') { + dependency.useTarget requested.group + ':' + requested.module + ':' + '${defaultBuildOptions().kotlinVersion}' + } + } + } + """.trimIndent() + + gradleBuildScript().appendText( + "\n" + """ + buildscript { + $resolutionStrategyHack + } + $resolutionStrategyHack + """.trimIndent() + ) + } +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/ConfigurationCacheIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/ConfigurationCacheIT.kt new file mode 100644 index 00000000000..dac77cf94ac --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/ConfigurationCacheIT.kt @@ -0,0 +1,118 @@ +/* + * Copyright 2010-2020 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.jetbrains.kotlin.gradle.util.findFileByName +import org.junit.Test +import java.io.File +import java.net.URI +import java.util.Arrays.asList +import kotlin.test.fail + +class ConfigurationCacheIT : AbstractConfigurationCacheIT() { + @Test + fun testSimpleKotlinJvmProject() = with(Project("kotlinProject")) { + testConfigurationCacheOf(":compileKotlin") + } + + @Test + fun testIncrementalKaptProject() = with(Project("kaptIncrementalCompilationProject")) { + setupIncrementalAptProject("AGGREGATING") + + testConfigurationCacheOf( + ":compileKotlin", + ":kaptKotlin", + buildOptions = defaultBuildOptions().copy( + incremental = true, + kaptOptions = KaptOptions( + verbose = true, + useWorkers = true, + incrementalKapt = true, + includeCompileClasspath = false + ) + ) + ) + } + + @Test + fun testInstantExecution() = with(Project("instantExecution")) { + testConfigurationCacheOf("assemble", executedTaskNames = asList(":lib-project:compileKotlin")) + } + + @Test + fun testInstantExecutionForJs() = with(Project("instantExecutionToJs")) { + testConfigurationCacheOf("assemble", executedTaskNames = asList(":compileKotlin2Js")) + } +} + +abstract class AbstractConfigurationCacheIT : BaseGradleIT() { + override fun defaultBuildOptions() = + super.defaultBuildOptions().copy(configurationCache = true) + + override val defaultGradleVersion: GradleVersionRequired = GradleVersionRequired.AtLeast("6.6-rc-3") + + protected fun Project.testConfigurationCacheOf( + vararg taskNames: String, + executedTaskNames: List? = null, + buildOptions: BuildOptions = defaultBuildOptions() + ) { + // First, run a build that serializes the tasks state for instant execution in further builds + + val executedTask: List = executedTaskNames ?: taskNames.toList() + + build(*taskNames, options = buildOptions) { + assertSuccessful() + assertTasksExecuted(executedTask) + assertContains("Calculating task graph as no configuration cache is available for tasks: ${taskNames.joinToString(separator = " ")}") + checkInstantExecutionSucceeded() + } + + build("clean") { + assertSuccessful() + } + + // Then run a build where tasks states are deserialized to check that they work correctly in this mode + build(*taskNames, options = buildOptions) { + assertSuccessful() + assertTasksExecuted(executedTask) + assertContains("Reusing configuration cache.") + } + + build(*taskNames, options = buildOptions) { + assertSuccessful() + assertTasksUpToDate(executedTask) + } + } + + private fun Project.checkInstantExecutionSucceeded() { + instantExecutionReportFile()?.let { htmlReportFile -> + fail("Instant execution problems were found, check ${htmlReportFile.asClickableFileUrl()} for details.") + } + } + + /** + * Copies all files from the directory containing the given [htmlReportFile] to a + * fresh temp dir and returns a reference to the copied [htmlReportFile] in the new + * directory. + */ + private fun copyReportToTempDir(htmlReportFile: File): File = + createTempDir().let { tempDir -> + htmlReportFile.parentFile.copyRecursively(tempDir) + tempDir.resolve(htmlReportFile.name) + } + + /** + * The instant execution report file, if exists, indicates problems were + * found while caching the task graph. + */ + private fun Project.instantExecutionReportFile() = projectDir + .resolve("configuration-cache") + .findFileByName("configuration-cache-report.html") + ?.let { copyReportToTempDir(it) } + + private fun File.asClickableFileUrl(): String = + URI("file", "", toURI().path, null, null).toString() +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/InstantExecutionIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/InstantExecutionIT.kt deleted file mode 100644 index 414f1414d03..00000000000 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/InstantExecutionIT.kt +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright 2010-2019 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.jetbrains.kotlin.gradle.util.AGPVersion -import org.jetbrains.kotlin.gradle.util.findFileByName -import org.jetbrains.kotlin.test.KotlinTestUtils -import org.junit.Test -import java.io.File -import java.net.URI -import kotlin.test.fail - -class InstantExecutionIT : BaseGradleIT() { - private val androidGradlePluginVersion: AGPVersion - get() = AGPVersion.v4_0_ALPHA_8 - - override fun defaultBuildOptions() = - super.defaultBuildOptions().copy( - androidHome = KotlinTestUtils.findAndroidSdk(), - androidGradlePluginVersion = androidGradlePluginVersion - ) - - private val minimumGradleVersion = GradleVersionRequired.AtLeast("6.1-rc-3") - - @Test - fun testSimpleKotlinJvmProject() = with(Project("kotlinProject", minimumGradleVersion)) { - testInstantExecutionOf(":compileKotlin") - } - - @Test - fun testSimpleKotlinAndroidProject() = with(Project("android-dagger", minimumGradleVersion, "kapt2")) { - applyAndroid40Alpha4KotlinVersionWorkaround() - projectDir.resolve("gradle.properties").appendText("\nkapt.incremental.apt=false") - testInstantExecutionOf(":app:compileDebugKotlin", ":app:kaptDebugKotlin", ":app:kaptGenerateStubsDebugKotlin") - } - - @Test - fun testIncrementalKaptProject() = with(getIncrementalKaptProject()) { - testInstantExecutionOf( - ":compileKotlin", - ":kaptKotlin", - buildOptions = defaultBuildOptions().copy( - incremental = true, - kaptOptions = KaptOptions( - verbose = true, - useWorkers = true, - incrementalKapt = true, - includeCompileClasspath = false - ) - ) - ) - } - - private fun getIncrementalKaptProject() = - Project("kaptIncrementalCompilationProject", minimumGradleVersion).apply { - setupIncrementalAptProject("AGGREGATING") - } - - private fun Project.testInstantExecutionOf(vararg taskNames: String, buildOptions: BuildOptions = defaultBuildOptions()) { - // First, run a build that serializes the tasks state for instant execution in further builds - instantExecutionOf(*taskNames, buildOptions = buildOptions) { - assertSuccessful() - assertTasksExecuted(*taskNames) - checkInstantExecutionSucceeded() - } - - build("clean") { - assertSuccessful() - } - - // Then run a build where tasks states are deserialized to check that they work correctly in this mode - instantExecutionOf(*taskNames, buildOptions = buildOptions) { - assertSuccessful() - assertTasksExecuted(*taskNames) - } - - instantExecutionOf(*taskNames, buildOptions = buildOptions) { - assertSuccessful() - assertTasksUpToDate(*taskNames) - } - } - - private fun Project.checkInstantExecutionSucceeded() { - instantExecutionReportFile()?.let { htmlReportFile -> - fail("Instant execution problems were found, check ${htmlReportFile.asClickableFileUrl()} for details.") - } - } - - private fun Project.instantExecutionOf( - vararg tasks: String, - buildOptions: BuildOptions = defaultBuildOptions(), - check: CompiledProject.() -> Unit - ) = - build("-Dorg.gradle.unsafe.instant-execution=true", *tasks, options = buildOptions, check = check) - - /** - * Copies all files from the directory containing the given [htmlReportFile] to a - * fresh temp dir and returns a reference to the copied [htmlReportFile] in the new - * directory. - */ - private fun copyReportToTempDir(htmlReportFile: File): File = - createTempDir().let { tempDir -> - htmlReportFile.parentFile.copyRecursively(tempDir) - tempDir.resolve(htmlReportFile.name) - } - - /** - * The instant execution report file, if exists, indicates problems were - * found while caching the task graph. - */ - private fun Project.instantExecutionReportFile() = projectDir - .resolve(".instant-execution-state") - .findFileByName("instant-execution-report.html") - ?.let { copyReportToTempDir(it) } - - private fun File.asClickableFileUrl(): String = - URI("file", "", toURI().path, null, null).toString() - - /** - * Android Gradle plugin 4.0-alpha4 depends on the EAP versions of some o.j.k modules. - * Force the current Kotlin version, so the EAP versions are not queried from the - * test project's repositories, where there's no 'kotlin-eap' repo. - * TODO remove this workaround once an Android Gradle plugin version is used that depends on the stable Kotlin version - */ - private fun Project.applyAndroid40Alpha4KotlinVersionWorkaround() { - setupWorkingDir() - - val resolutionStrategyHack = """ - configurations.all { - resolutionStrategy.dependencySubstitution.all { dependency -> - def requested = dependency.requested - if (requested instanceof ModuleComponentSelector && requested.group == 'org.jetbrains.kotlin') { - dependency.useTarget requested.group + ':' + requested.module + ':' + '${defaultBuildOptions().kotlinVersion}' - } - } - } - """.trimIndent() - - gradleBuildScript().appendText("\n" + """ - buildscript { - $resolutionStrategyHack - } - $resolutionStrategyHack - """.trimIndent()) - } -} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinGradlePluginIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinGradlePluginIT.kt index 8cf754d7558..c7ca33d8a89 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinGradlePluginIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinGradlePluginIT.kt @@ -365,7 +365,7 @@ class KotlinGradleIT : BaseGradleIT() { } @Test - fun testDowngradeTo106() { + fun testDowngradePluginVersion() { val project = Project("kotlinProject") val options = defaultBuildOptions().copy(incremental = true, withDaemon = false) @@ -373,7 +373,7 @@ class KotlinGradleIT : BaseGradleIT() { assertSuccessful() } - project.build("clean", "assemble", options = options.copy(kotlinVersion = "1.0.6")) { + project.build("clean", "assemble", options = options.copy(kotlinVersion = "1.3.41")) { assertSuccessful() } } diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/UpToDateIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/UpToDateIT.kt index b3a92624e27..d1519f0d513 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/UpToDateIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/UpToDateIT.kt @@ -78,7 +78,9 @@ class UpToDateIT : BaseGradleIT() { val originalPaths get() = originalCompilerCp.map { it.replace("\\", "/") }.joinToString(", ") { "'$it'" } override fun initProject(project: Project) = with(project) { - buildGradle.appendText("\nprintln 'compiler_cp=' + compileKotlin.getComputedCompilerClasspath\$kotlin_gradle_plugin()") + buildGradle.appendText( + "\nafterEvaluate { println 'compiler_cp=' + compileKotlin.getComputedCompilerClasspath\$kotlin_gradle_plugin() }" + ) build("clean") { originalCompilerCp = "compiler_cp=\\[(.*)]".toRegex().find(output)!!.groupValues[1].split(", ") } buildGradle.appendText("""${'\n'} // Add Kapt to the project to test its input checks as well: diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/util/AGPVersion.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/util/AGPVersion.kt index 4b60d05f71a..5f65711949d 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/util/AGPVersion.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/util/AGPVersion.kt @@ -24,6 +24,7 @@ class AGPVersion private constructor(private val versionNumber: VersionNumber) { val v3_3_2 = fromString("3.3.2") val v3_4_1 = fromString("3.4.1") val v3_6_0 = fromString("3.6.0") - val v4_0_ALPHA_8 = fromString("4.0.0-alpha08") + val v4_1_0 = fromString("4.1.0-beta02") + val v4_2_0 = fromString("4.2.0-alpha07") } } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/instantExecution/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/instantExecution/build.gradle new file mode 100644 index 00000000000..f37b00da1c9 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/instantExecution/build.gradle @@ -0,0 +1,16 @@ +buildscript { + repositories { + mavenLocal() + mavenCentral() + } + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +subprojects { + repositories { + mavenLocal() + mavenCentral() + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/instantExecution/lib-project/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/instantExecution/lib-project/build.gradle new file mode 100644 index 00000000000..ee7434e1b5d --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/instantExecution/lib-project/build.gradle @@ -0,0 +1,5 @@ +apply plugin: 'kotlin' + +dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/instantExecution/lib-project/src/main/kotlin/libproject/Util.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/instantExecution/lib-project/src/main/kotlin/libproject/Util.kt new file mode 100644 index 00000000000..3b01f5d571f --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/instantExecution/lib-project/src/main/kotlin/libproject/Util.kt @@ -0,0 +1,10 @@ +/* + * Copyright 2010-2020 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 libproject + +class Util { + val projectName: String = "lib-project" +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/instantExecution/main-project/src/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/instantExecution/main-project/src/build.gradle new file mode 100644 index 00000000000..b3a0aaed263 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/instantExecution/main-project/src/build.gradle @@ -0,0 +1,6 @@ +apply plugin: 'kotlin' + +dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + compile project(':lib-project') +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/instantExecution/main-project/src/main/kotlin/Main.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/instantExecution/main-project/src/main/kotlin/Main.kt new file mode 100644 index 00000000000..65cc6420e5d --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/instantExecution/main-project/src/main/kotlin/Main.kt @@ -0,0 +1,10 @@ +import libProject.Util + +/* + * Copyright 2010-2020 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. + */ + +fun main(args: Array) { + println(Util.projectName) +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/instantExecution/settings.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/instantExecution/settings.gradle new file mode 100644 index 00000000000..5e7a37c6aec --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/instantExecution/settings.gradle @@ -0,0 +1 @@ +include ':main-project', ':lib-project' \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/instantExecutionToJs/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/instantExecutionToJs/build.gradle new file mode 100644 index 00000000000..c9c914635f1 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/instantExecutionToJs/build.gradle @@ -0,0 +1,37 @@ +buildscript { + repositories { + mavenLocal() + mavenCentral() + } + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +apply plugin: 'kotlin2js' + +repositories { + mavenLocal() + mavenCentral() +} + +dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib-js:$kotlin_version" + testCompile "org.jetbrains.kotlin:kotlin-test-js:$kotlin_version" +} + +task jarSources(type: Jar) { + from sourceSets.main.allSource + classifier = 'source' +} +artifacts { + compile jarSources +} + +compileKotlin2Js.kotlinOptions.outputFile = "${buildDir}/kotlin2js/main/module.js" +compileTestKotlin2Js.kotlinOptions.outputFile = "${buildDir}/kotlin2js/test/module-tests.js" + +if (project.findProperty("kotlin.js.useIrBackend")?.toBoolean() == true) { + compileKotlin2Js.kotlinOptions.freeCompilerArgs += ["-Xir-produce-klib-dir", "-Xir-only"] + compileTestKotlin2Js.kotlinOptions.freeCompilerArgs += ["-Xir-produce-js"] +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/instantExecutionToJs/src/main/kotlin/project/Main.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/instantExecutionToJs/src/main/kotlin/project/Main.kt new file mode 100644 index 00000000000..7fce923eb4a --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/instantExecutionToJs/src/main/kotlin/project/Main.kt @@ -0,0 +1,10 @@ +/* + * Copyright 2010-2020 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 project + +class Main { + fun value(): String = "instant" +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/instantExecutionToJs/src/test/kotlin/MainTest.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/instantExecutionToJs/src/test/kotlin/MainTest.kt new file mode 100644 index 00000000000..115ce635853 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/instantExecutionToJs/src/test/kotlin/MainTest.kt @@ -0,0 +1,13 @@ +/* + * Copyright 2010-2020 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. + */ +import project.Main +import kotlin.test.* + +class MainTest { + @Test + fun mySimpleTest() { + assertEquals("instant", Main().value()) + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-android/lib1/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-android/lib1/build.gradle index 62aefd33f7f..3386877364c 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-android/lib1/build.gradle +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-android/lib1/build.gradle @@ -2,12 +2,12 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' android { - compileSdkVersion 29 - buildToolsVersion "29.0.2" + compileSdkVersion 26 + buildToolsVersion "28.0.3" defaultConfig { - minSdkVersion 21 - targetSdkVersion 29 + minSdkVersion 26 + targetSdkVersion 26 } } diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-android/lib2/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-android/lib2/build.gradle index 61eb714045d..70bc51e3241 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-android/lib2/build.gradle +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-35942-android/lib2/build.gradle @@ -2,12 +2,12 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' android { - compileSdkVersion 29 - buildToolsVersion "29.0.2" + compileSdkVersion 26 + buildToolsVersion "28.0.3" defaultConfig { - minSdkVersion 21 - targetSdkVersion 29 + minSdkVersion 26 + targetSdkVersion 26 } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleCompilerRunnerWithWorkers.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleCompilerRunnerWithWorkers.kt index 9c3fc28fedc..e13c8555f6b 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleCompilerRunnerWithWorkers.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleCompilerRunnerWithWorkers.kt @@ -5,17 +5,18 @@ package org.jetbrains.kotlin.compilerRunner -import org.gradle.api.Task import org.gradle.workers.IsolationMode import org.gradle.workers.WorkerExecutor import org.jetbrains.kotlin.gradle.logging.kotlinDebug +import org.jetbrains.kotlin.gradle.tasks.GradleCompileTaskProvider internal class GradleCompilerRunnerWithWorkers( - task: Task, + taskProvider: GradleCompileTaskProvider, private val workersExecutor: WorkerExecutor -) : GradleCompilerRunner(task) { +) : GradleCompilerRunner(taskProvider) { + override fun runCompilerAsync(workArgs: GradleKotlinCompilerWorkArguments) { - task.logger.kotlinDebug { "Starting Kotlin compiler work from task '${task.path}'" } + loggerProvider.kotlinDebug { "Starting Kotlin compiler work from task '${pathProvider}'" } // todo: write tests with Workers enabled; workersExecutor.submit(GradleKotlinCompilerWork::class.java) { config -> config.isolationMode = IsolationMode.NONE diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinCompilerRunner.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinCompilerRunner.kt index 88867593723..5128c566317 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinCompilerRunner.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinCompilerRunner.kt @@ -6,8 +6,8 @@ package org.jetbrains.kotlin.compilerRunner import org.gradle.api.Project -import org.gradle.api.Task import org.gradle.api.invocation.Gradle +import org.gradle.api.logging.Logger import org.gradle.api.plugins.JavaPluginConvention import org.gradle.api.tasks.bundling.AbstractArchiveTask import org.gradle.jvm.tasks.Jar @@ -24,11 +24,12 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinWithJavaTarget import org.jetbrains.kotlin.gradle.plugin.mpp.isMain import org.jetbrains.kotlin.gradle.plugin.mpp.ownModuleName import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService +import org.jetbrains.kotlin.gradle.tasks.GradleCompileTaskProvider import org.jetbrains.kotlin.gradle.tasks.KotlinCompileTaskData import org.jetbrains.kotlin.gradle.tasks.locateTask import org.jetbrains.kotlin.gradle.utils.archivePathCompatible import org.jetbrains.kotlin.gradle.utils.newTmpFile -import org.jetbrains.kotlin.gradle.utils.relativeToRoot +import org.jetbrains.kotlin.gradle.utils.relativeOrCanonical import org.jetbrains.kotlin.incremental.IncrementalModuleEntry import org.jetbrains.kotlin.incremental.IncrementalModuleInfo import org.jetbrains.kotlin.statistics.metrics.BooleanMetrics @@ -50,9 +51,21 @@ const val COULD_NOT_CONNECT_TO_DAEMON_MESSAGE = "Could not connect to Kotlin com internal fun kotlinCompilerExecutionStrategy(): String = System.getProperty(KOTLIN_COMPILER_EXECUTION_STRATEGY_PROPERTY) ?: DAEMON_EXECUTION_STRATEGY -internal open class GradleCompilerRunner(protected val task: Task) { - protected val project: Project - get() = task.project +/* +Using real taskProvider cause "field 'taskProvider' from type 'org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner': +value 'fixed(class org.jetbrains.kotlin.gradle.tasks.KotlinCompile_Decorated, task ':compileKotlin')' +is not assignable to 'org.gradle.api.tasks.TaskProvider'" exception + */ +internal open class GradleCompilerRunner(protected val taskProvider: GradleCompileTaskProvider) { + + internal val pathProvider = taskProvider.path + internal val loggerProvider = taskProvider.logger + internal val buildDirProvider = taskProvider.buildDir + internal val projectDirProvider = taskProvider.projectDir + internal val projectRootDirProvider = taskProvider.rootDir + internal val sessionDirProvider = taskProvider.sessionsDir + internal val projectNameProvider = taskProvider.projectName + internal val incrementalModuleInfoProvider = taskProvider.buildModulesInfo /** * Compiler might be executed asynchronously. Do not do anything requiring end of compilation after this function is called. @@ -107,7 +120,7 @@ internal open class GradleCompilerRunner(protected val task: Task) { environment: GradleCompilerEnvironment ) { if (compilerArgs.version) { - task.logger.lifecycle( + loggerProvider.lifecycle( "Kotlin version " + loadCompilerVersion(environment.compilerClasspath) + " (JRE " + System.getProperty("java.runtime.version") + ")" ) @@ -128,9 +141,16 @@ internal open class GradleCompilerRunner(protected val task: Task) { } val incrementalCompilationEnvironment = environment.incrementalCompilationEnvironment - val modulesInfo = incrementalCompilationEnvironment?.let { buildModulesInfo(project.gradle) } + val modulesInfo = incrementalCompilationEnvironment?.let { incrementalModuleInfoProvider } val workArgs = GradleKotlinCompilerWorkArguments( - projectFiles = ProjectFilesForCompilation(project), + projectFiles = ProjectFilesForCompilation( + loggerProvider, + projectDirProvider, + buildDirProvider, + projectNameProvider, + projectRootDirProvider, + sessionDirProvider + ), compilerFullClasspath = environment.compilerFullClasspath, compilerClassName = compilerClassName, compilerArgs = argsArray, @@ -138,12 +158,12 @@ internal open class GradleCompilerRunner(protected val task: Task) { incrementalCompilationEnvironment = incrementalCompilationEnvironment, incrementalModuleInfo = modulesInfo, outputFiles = environment.outputFiles.toList(), - taskPath = task.path, + taskPath = pathProvider, buildReportMode = environment.buildReportMode, kotlinScriptExtensions = environment.kotlinScriptExtensions, allWarningsAsErrors = compilerArgs.allWarningsAsErrors ) - TaskLoggers.put(task.path, task.logger) + TaskLoggers.put(pathProvider, loggerProvider) runCompilerAsync(workArgs) } @@ -221,7 +241,7 @@ internal open class GradleCompilerRunner(protected val task: Task) { } else { if (target is KotlinWithJavaTarget<*>) { val jar = project.tasks.getByName(target.artifactsTaskName) as Jar - jarToClassListFile[jar.archivePathCompatible.canonicalFile] = target.defaultArtifactClassesListFile + jarToClassListFile[jar.archivePathCompatible.canonicalFile] = target.defaultArtifactClassesListFile.get() } } } @@ -261,11 +281,9 @@ internal open class GradleCompilerRunner(protected val task: Task) { private var clientIsAliveFlagFile: File? = null @Synchronized - internal fun getOrCreateClientFlagFile(project: Project): File { - val log = project.logger + internal fun getOrCreateClientFlagFile(log: Logger, projectName: String): File { if (clientIsAliveFlagFile == null || !clientIsAliveFlagFile!!.exists()) { - val projectName = project.rootProject.name.normalizeForFlagFile() - clientIsAliveFlagFile = newTmpFile(prefix = "kotlin-compiler-in-$projectName-", suffix = ".alive") + clientIsAliveFlagFile = newTmpFile(prefix = "kotlin-compiler-in-${projectName}-", suffix = ".alive") log.kotlinDebug { CREATED_CLIENT_FILE_PREFIX + clientIsAliveFlagFile!!.canonicalPath } } else { log.kotlinDebug { EXISTING_CLIENT_FILE_PREFIX + clientIsAliveFlagFile!!.canonicalPath } @@ -274,7 +292,7 @@ internal open class GradleCompilerRunner(protected val task: Task) { return clientIsAliveFlagFile!! } - private fun String.normalizeForFlagFile(): String { + internal fun String.normalizeForFlagFile(): String { val validChars = ('a'..'z') + ('0'..'9') + "-_" return filter { it in validChars } } @@ -285,14 +303,13 @@ internal open class GradleCompilerRunner(protected val task: Task) { // session files are deleted at org.jetbrains.kotlin.gradle.plugin.KotlinGradleBuildServices.buildFinished @Synchronized - internal fun getOrCreateSessionFlagFile(project: Project): File { - val log = project.logger + internal fun getOrCreateSessionFlagFile(log: Logger, sessionsDir: File, projectRootDir: File): File { if (sessionFlagFile == null || !sessionFlagFile!!.exists()) { - val sessionFilesDir = sessionsDir(project).apply { mkdirs() } + val sessionFilesDir = sessionsDir.apply { mkdirs() } sessionFlagFile = newTmpFile(prefix = "kotlin-compiler-", suffix = ".salive", directory = sessionFilesDir) - log.kotlinDebug { CREATED_SESSION_FILE_PREFIX + sessionFlagFile!!.relativeToRoot(project) } + log.kotlinDebug { CREATED_SESSION_FILE_PREFIX + sessionFlagFile!!.relativeOrCanonical(projectRootDir) } } else { - log.kotlinDebug { EXISTING_SESSION_FILE_PREFIX + sessionFlagFile!!.relativeToRoot(project) } + log.kotlinDebug { EXISTING_SESSION_FILE_PREFIX + sessionFlagFile!!.relativeOrCanonical(projectRootDir) } } return sessionFlagFile!! diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinCompilerWork.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinCompilerWork.kt index 0917fcfed73..e728e2bc51d 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinCompilerWork.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinCompilerWork.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.compilerRunner import org.gradle.api.Project +import org.gradle.api.logging.Logger import org.jetbrains.kotlin.cli.common.ExitCode import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.config.Services @@ -35,11 +36,12 @@ internal class ProjectFilesForCompilation( val sessionFlagFile: File, val buildDir: File ) : Serializable { - constructor(project: Project) : this( - projectRootFile = project.rootProject.projectDir, - clientIsAliveFlagFile = GradleCompilerRunner.getOrCreateClientFlagFile(project), - sessionFlagFile = GradleCompilerRunner.getOrCreateSessionFlagFile(project), - buildDir = project.buildDir + //TODO + constructor(logger: Logger, projectDir:File, buildDir: File, prjectName: String, projectRootDir: File, sessionDir: File) : this( + projectRootFile = projectDir, + clientIsAliveFlagFile = GradleCompilerRunner.getOrCreateClientFlagFile(logger, prjectName), + sessionFlagFile = GradleCompilerRunner.getOrCreateSessionFlagFile(logger, sessionDir, projectRootDir), + buildDir = buildDir ) companion object { diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/CompilerArgumentsContributor.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/CompilerArgumentsContributor.kt index fcb398a1b70..450f8d52493 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/CompilerArgumentsContributor.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/CompilerArgumentsContributor.kt @@ -5,13 +5,15 @@ package org.jetbrains.kotlin.gradle.internal -import org.gradle.api.tasks.TaskProvider -import org.jetbrains.kotlin.cli.common.arguments.* -import org.jetbrains.kotlin.gradle.dsl.* +import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments +import org.jetbrains.kotlin.cli.common.arguments.CommonToolArguments +import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments +import org.jetbrains.kotlin.gradle.dsl.Coroutines +import org.jetbrains.kotlin.gradle.dsl.fillDefaultValues import org.jetbrains.kotlin.gradle.logging.kotlinDebug -import org.jetbrains.kotlin.gradle.tasks.* -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile -import org.jetbrains.kotlin.gradle.utils.getValue +import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile +import org.jetbrains.kotlin.gradle.tasks.KotlinCompileArgumentsProvider +import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompilerArgumentsProvider import org.jetbrains.kotlin.gradle.utils.toSortedPathsArray import org.jetbrains.kotlin.incremental.classpathAsList import org.jetbrains.kotlin.incremental.destinationAsFile @@ -38,22 +40,20 @@ internal fun compilerArgumentsConfigurationFlags(defaultsOnly: Boolean, ignoreCl * but outside the tasks, so that this state & logic can be reused without referencing the task directly. */ internal open class AbstractKotlinCompileArgumentsContributor( // Don't save this reference into a property! That would be hostile to Gradle instant execution - taskProvider: TaskProvider> + taskProvider: KotlinCompileArgumentsProvider> ) : CompilerArgumentsContributor { - private val coroutines by taskProvider.map { it.coroutines } - protected val logger by taskProvider.map { it.logger } - - private val isMultiplatform by taskProvider.map { it.isMultiplatform } - - private val pluginClasspath by taskProvider.map { it.pluginClasspath } - private val pluginOptions by taskProvider.map { it.pluginOptions } + private val coroutines = taskProvider.coroutines + protected val logger = taskProvider.logger + private val isMultiplatform = taskProvider.isMultiplatform + private val pluginClasspath = taskProvider.pluginClasspath + private val pluginOptions = taskProvider.pluginOptions override fun contributeArguments( args: T, flags: Collection ) { - args.coroutinesState = when (coroutines) { + args.coroutinesState = when (coroutines.get()) { Coroutines.ENABLE -> CommonCompilerArguments.ENABLE Coroutines.WARN -> CommonCompilerArguments.WARN Coroutines.ERROR -> CommonCompilerArguments.ERROR @@ -79,23 +79,14 @@ internal open class AbstractKotlinCompileArgumentsContributor + taskProvider: KotlinJvmCompilerArgumentsProvider ) : AbstractKotlinCompileArgumentsContributor(taskProvider) { - private val moduleName by taskProvider.map { it.moduleName } - - private val friendPaths by taskProvider.map { it.friendPaths } - - private val compileClasspath by taskProvider.map { it.compileClasspath } - - private val destinationDir by taskProvider.map { it.destinationDir } - - private val kotlinOptions by taskProvider.map { - listOfNotNull( - it.parentKotlinOptionsImpl as KotlinJvmOptionsImpl?, - it.kotlinOptions as KotlinJvmOptionsImpl - ) - } + private val moduleName = taskProvider.moduleName + private val friendPaths = taskProvider.friendPaths + private val compileClasspath = taskProvider.compileClasspath + private val destinationDir = taskProvider.destinationDir + private val kotlinOptions = taskProvider.kotlinOptions override fun contributeArguments( args: K2JVMCompilerArguments, @@ -121,6 +112,6 @@ internal open class KotlinJvmCompilerArgumentsContributor( } args.destinationAsFile = destinationDir - kotlinOptions.forEach { it.updateArguments(args) } + kotlinOptions.forEach { it?.updateArguments(args) } } } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/Kapt3KotlinGradleSubplugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/Kapt3KotlinGradleSubplugin.kt index 836813e9270..34fbb6d7459 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/Kapt3KotlinGradleSubplugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/Kapt3KotlinGradleSubplugin.kt @@ -10,6 +10,7 @@ import com.android.build.gradle.api.AndroidSourceSet import com.android.build.gradle.api.BaseVariant import com.android.build.gradle.api.SourceKind import com.intellij.openapi.util.SystemInfo +import com.intellij.util.lang.JavaVersion import org.gradle.api.GradleException import org.gradle.api.Project import org.gradle.api.artifacts.Configuration @@ -22,7 +23,6 @@ import org.gradle.api.tasks.* import org.gradle.api.tasks.compile.AbstractCompile import org.gradle.api.tasks.compile.JavaCompile import org.gradle.process.CommandLineArgumentProvider -import org.gradle.tooling.provider.model.ToolingModelBuilder import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions import org.jetbrains.kotlin.gradle.internal.kapt.incremental.CLASS_STRUCTURE_ARTIFACT_TYPE @@ -34,6 +34,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.jetbrains.kotlin.gradle.tasks.KotlinCompileTaskData import org.jetbrains.kotlin.gradle.tasks.locateTask import org.jetbrains.kotlin.gradle.tasks.registerTask +import org.jetbrains.kotlin.gradle.utils.isConfigurationCacheAvailable import java.io.ByteArrayOutputStream import java.io.File import java.io.ObjectOutputStream @@ -474,7 +475,14 @@ class Kapt3GradleSubplugin @Inject internal constructor(private val registry: To val dslJavacOptions: Provider> = project.provider { kaptExtension.getJavacOptions().toMutableMap().also { result -> if (javaCompile != null && "-source" !in result && "--source" !in result && "--release" !in result) { - val sourceOptionKey = if (SystemInfo.isJavaVersionAtLeast(12, 0, 0)) { + val atLeast12Java = + if (isConfigurationCacheAvailable(project.gradle)) { + val currentJavaVersion = JavaVersion.parse(project.providers.systemProperty("java.version").forUseAtConfigurationTime().get()) + currentJavaVersion.feature >= 12 + } else { + SystemInfo.isJavaVersionAtLeast(12, 0, 0) + } + val sourceOptionKey = if (atLeast12Java) { "--source" } else { "-source" diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptGenerateStubsTask.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptGenerateStubsTask.kt index d3cce4c560b..64ff1696b1c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptGenerateStubsTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptGenerateStubsTask.kt @@ -20,7 +20,7 @@ import org.gradle.api.artifacts.Configuration import org.gradle.api.file.FileCollection import org.gradle.api.tasks.* import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments -import org.jetbrains.kotlin.gradle.dsl.* +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptionsImpl import org.jetbrains.kotlin.gradle.tasks.FilteringSourceRootsContainer import org.jetbrains.kotlin.gradle.tasks.KotlinCompile @@ -50,7 +50,7 @@ open class KaptGenerateStubsTask : KotlinCompile() { @get:Classpath @get:InputFiles val kaptClasspath: FileCollection - get() = project.files(kaptClasspathConfigurations) + get() = objects.fileCollection().from(kaptClasspathConfigurations) @get:Internal internal lateinit var kaptClasspathConfigurations: List @@ -69,6 +69,9 @@ open class KaptGenerateStubsTask : KotlinCompile() { error("KaptGenerateStubsTask.useModuleDetection setter should not be called!") } + @get:Input + val verbose = (project.hasProperty("kapt.verbose") && project.property("kapt.verbose").toString().toBoolean() == true) + override fun source(vararg sources: Any): SourceTask { return super.source(sourceRootsContainer.add(sources)) } @@ -95,7 +98,7 @@ open class KaptGenerateStubsTask : KotlinCompile() { val pluginOptionsWithKapt = pluginOptions.withWrappedKaptOptions(withApClasspath = kaptClasspath) args.pluginOptions = (pluginOptionsWithKapt.arguments + args.pluginOptions!!).toTypedArray() - args.verbose = project.hasProperty("kapt.verbose") && project.property("kapt.verbose").toString().toBoolean() == true + args.verbose = verbose args.classpathAsList = this.compileClasspath.filter { it.exists() }.toList() args.destinationAsFile = this.destinationDir } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptTask.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptTask.kt index 21948a834fd..178c2f1aa0c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptTask.kt @@ -1,6 +1,5 @@ package org.jetbrains.kotlin.gradle.internal -import com.intellij.openapi.util.io.FileUtil import org.gradle.api.artifacts.Configuration import org.gradle.api.file.FileCollection import org.gradle.api.internal.ConventionTask @@ -28,7 +27,7 @@ abstract class KaptTask : ConventionTask(), TaskWithLocalState { outputs.cacheIf(reason) { useBuildCache } } - override fun localStateDirectories(): FileCollection = project.files() + override fun localStateDirectories(): FileCollection = objects.fileCollection() @get:Internal @field:Transient @@ -37,10 +36,13 @@ abstract class KaptTask : ConventionTask(), TaskWithLocalState { @get:Internal internal lateinit var stubsDir: File + @get:Internal + internal val objects = project.objects + @get:Classpath @get:InputFiles val kaptClasspath: FileCollection - get() = project.files(kaptClasspathConfigurations) + get() = objects.fileCollection().from(kaptClasspathConfigurations) @get:Classpath @get:InputFiles @@ -137,8 +139,36 @@ abstract class KaptTask : ConventionTask(), TaskWithLocalState { private fun isRootAllowed(file: File): Boolean = file.exists() && - !FileUtil.isAncestor(destinationDir, file, /* strict = */ false) && - !FileUtil.isAncestor(classesDir, file, /* strict = */ false) + !isAncestor(destinationDir, file) && + !isAncestor(classesDir, file) + + //Have to avoid using FileUtil because it is required system property reading that is not allowed for configuration cache + private fun isAncestor(dir: File, file: File): Boolean { + val path = file.canonicalPath + val prefix = dir.canonicalPath + val pathLength = path.length + val prefixLength = prefix.length + //TODO + val caseSensitive = true + return if (prefixLength == 0) { + true + } else if (prefixLength > pathLength) { + false + } else if (!path.regionMatches(0, prefix, 0, prefixLength, ignoreCase = !caseSensitive)) { + return false + } else if (pathLength == prefixLength) { + return true + } else { + val lastPrefixChar: Char = prefix.get(prefixLength - 1) + var slashOrSeparatorIdx = prefixLength + if (lastPrefixChar == '/' || lastPrefixChar == File.separatorChar) { + slashOrSeparatorIdx = prefixLength - 1 + } + val next1 = path[slashOrSeparatorIdx] + return !(next1 != '/' && next1 != File.separatorChar) + } + } + private fun FileCollection?.orEmpty(): FileCollection = this ?: project.files() diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptWithKotlincTask.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptWithKotlincTask.kt index b12f89629bc..b1d21e872fa 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptWithKotlincTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptWithKotlincTask.kt @@ -23,6 +23,7 @@ import org.jetbrains.kotlin.gradle.logging.GradleKotlinLogger import org.jetbrains.kotlin.gradle.logging.GradlePrintingMessageCollector import org.jetbrains.kotlin.gradle.plugin.PLUGIN_CLASSPATH_CONFIGURATION_NAME import org.jetbrains.kotlin.gradle.tasks.CompilerPluginOptions +import org.jetbrains.kotlin.gradle.tasks.GradleCompileTaskProvider import org.jetbrains.kotlin.gradle.utils.getValue import org.jetbrains.kotlin.gradle.utils.optionalProvider import org.jetbrains.kotlin.gradle.utils.toSortedPathsArray @@ -43,6 +44,9 @@ open class KaptWithKotlincTask : KaptTask(), CompilerArgumentAwareWithInput by project.provider { + val kaptJars: Collection by lazy { project.configurations.getByName(KAPT_WORKER_DEPENDENCIES_CONFIGURATION_NAME).resolve() } @@ -48,6 +47,18 @@ open class KaptWithoutKotlincTask @Inject constructor(private val workerExecutor @get:Input lateinit var javacOptions: Map + @get:Input + internal val kotlinAndroidPluginWrapperPluginDoesNotExist = project.plugins.none { it is KotlinAndroidPluginWrapper } + + @get:Input + internal val kotlinStdlibClasspath = findKotlinStdlibClasspath(project) + + @get:Internal + internal val projectDir = project.projectDir + + @get:Internal + internal val providers = project.providers + private fun getAnnotationProcessorOptions(): Map { val options = processorOptions.subpluginOptionsByPluginId[Kapt3GradleSubplugin.KAPT_SUBPLUGIN_ID] ?: return emptyMap() @@ -70,7 +81,7 @@ open class KaptWithoutKotlincTask @Inject constructor(private val workerExecutor } val compileClasspath = classpath.files.toMutableList() - if (project.plugins.none { it is KotlinAndroidPluginWrapper }) { + if (kotlinAndroidPluginWrapperPluginDoesNotExist) { compileClasspath.addAll(0, PathUtil.getJdkClassesRootsFromCurrentJre()) } @@ -82,7 +93,7 @@ open class KaptWithoutKotlincTask @Inject constructor(private val workerExecutor } val optionsForWorker = KaptOptionsForWorker( - project.projectDir, + projectDir, compileClasspath, javaSourceRoots.toList(), @@ -108,25 +119,35 @@ open class KaptWithoutKotlincTask @Inject constructor(private val workerExecutor if (annotationProcessorFqNames.isEmpty() && kaptClasspath.isEmpty()) return - val kaptClasspath = kaptJars + findKotlinStdlibClasspath(project) + val kaptClasspath = kaptJars + kotlinStdlibClasspath workerExecutor.submit(KaptExecution::class.java) { config -> - val isolationModeStr = project.findProperty("kapt.workers.isolation") as String? ?: "none" + //TODO for gradle < 6.5 + val isolationModeStr = getValue("kapt.workers.isolation") ?: "none" config.isolationMode = when (isolationModeStr.toLowerCase()) { "process" -> IsolationMode.PROCESS "none" -> IsolationMode.NONE else -> IsolationMode.NONE } config.params(optionsForWorker, findToolsJar()?.toURI()?.toURL()?.toString().orEmpty(), kaptClasspath) - if (project.findProperty("kapt.workers.log.classloading") == "true") { + if (getValue("kapt.workers.log.classloading") == "true") { // for tests config.forkOptions.jvmArgs("-verbose:class") } logger.info("Kapt worker classpath: ${config.classpath}") } } + + internal fun getValue(propertyName: String): String? = + if (isGradleVersionAtLeast(6, 5)) { + providers.systemProperty(propertyName).forUseAtConfigurationTime().orNull + } else { + project.findProperty(propertyName) as String? + } + } + private class KaptExecution @Inject constructor( val optionsForWorker: KaptOptionsForWorker, val toolsJarURLSpec: String, diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/model/builder/KotlinModelBuilder.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/model/builder/KotlinModelBuilder.kt index 2b96d58b884..66dd5de4413 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/model/builder/KotlinModelBuilder.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/model/builder/KotlinModelBuilder.kt @@ -144,7 +144,7 @@ class KotlinModelBuilder(private val kotlinPluginVersion: String, private val an } private fun AbstractKotlinCompile<*>.createExperimentalFeatures(): ExperimentalFeatures { - return ExperimentalFeaturesImpl(coroutinesStr) + return ExperimentalFeaturesImpl(coroutinesStr.get()) } } } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/BuildEventsListenerRegistryHolder.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/BuildEventsListenerRegistryHolder.kt new file mode 100644 index 00000000000..ab2cf2c716c --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/BuildEventsListenerRegistryHolder.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2010-2020 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.plugin + +import org.gradle.api.Project +import org.gradle.build.event.BuildEventsListenerRegistry +import org.jetbrains.kotlin.gradle.utils.isConfigurationCacheAvailable +import javax.inject.Inject + +open class BuildEventsListenerRegistryHolder @Inject constructor(val listenerRegistry: BuildEventsListenerRegistry?) { + companion object { + fun getInstance(project: Project) = run { + if (isConfigurationCacheAvailable(project.gradle)) { + project.objects.newInstance(BuildEventsListenerRegistryHolder::class.java) + } else { + BuildEventsListenerRegistryHolder(null) + } + } + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleBuildListener.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleBuildListener.kt new file mode 100644 index 00000000000..90008524041 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleBuildListener.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2010-2020 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.plugin + +import org.gradle.tooling.events.FinishEvent +import org.gradle.tooling.events.OperationCompletionListener + +//Available since Gradle 6.1 +class KotlinGradleBuildListener( +// val gradle: Gradle, + val services: KotlinGradleFinishBuildHandler +) : OperationCompletionListener { + + override fun onFinish(event: FinishEvent) { +// services.buildFinished(gradle) + } + +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleBuildServices.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleBuildServices.kt index e3a83f2de7f..8c9e6ee89dc 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleBuildServices.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleBuildServices.kt @@ -10,20 +10,18 @@ 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.compilerRunner.DELETED_SESSION_FILE_PREFIX -import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner import org.jetbrains.kotlin.gradle.logging.kotlinDebug import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskExecutionResults import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskLoggers import org.jetbrains.kotlin.gradle.report.configureBuildReporter -import org.jetbrains.kotlin.gradle.utils.relativeToRoot -import org.jetbrains.kotlin.utils.addToStdlib.sumByLong -import java.lang.management.ManagementFactory -import kotlin.math.max +import org.jetbrains.kotlin.gradle.utils.isConfigurationCacheAvailable + +//Support Gradle 6 and less. Move to internal class KotlinGradleBuildServices private constructor( private val gradle: Gradle ) : BuildAdapter() { + companion object { private val CLASS_NAME = KotlinGradleBuildServices::class.java.simpleName const val FORCE_SYSTEM_GC_MESSAGE = "Forcing System.gc()" @@ -34,36 +32,69 @@ internal class KotlinGradleBuildServices private constructor( val ALREADY_INITIALIZED_MESSAGE = "$CLASS_NAME is already initialized" @field:Volatile - private var instance: KotlinGradleBuildServices? = null + internal var instance: KotlinGradleBuildServices? = null + +// @JvmStatic +// @Synchronized +// fun getInstance(gradle: Gradle): KotlinGradleBuildServices { +// val log = Logging.getLogger(KotlinGradleBuildServices::class.java) +// +// if (instance != null) { +// log.kotlinDebug(ALREADY_INITIALIZED_MESSAGE) +// return instance!! +// } +// +// val services = KotlinGradleBuildServices(gradle) +// instance = services +// if (!isGradleVersionAtLeast(6,1)) { +// gradle.addBuildListener(services) +// log.kotlinDebug(INIT_MESSAGE) +// } else { +// BuildEventsListenerRegistry. +// } +// +// services.buildStarted() +// return services +// } + @JvmStatic @Synchronized - fun getInstance(gradle: Gradle): KotlinGradleBuildServices { + fun getInstance(project: Project, listenerRegistryHolder: BuildEventsListenerRegistryHolder): KotlinGradleBuildServices { val log = Logging.getLogger(KotlinGradleBuildServices::class.java) + val kotlinGradleListenerProvider: org.gradle.api.provider.Provider = project.provider { + KotlinGradleBuildListener(KotlinGradleFinishBuildHandler()) + } if (instance != null) { log.kotlinDebug(ALREADY_INITIALIZED_MESSAGE) return instance!! } + val gradle = project.gradle val services = KotlinGradleBuildServices(gradle) - gradle.addBuildListener(services) - instance = services - log.kotlinDebug(INIT_MESSAGE) + if (isConfigurationCacheAvailable(gradle)) { + listenerRegistryHolder.listenerRegistry!!.onTaskCompletion(kotlinGradleListenerProvider) + } else { + gradle.addBuildListener(services) + instance = services + log.kotlinDebug(INIT_MESSAGE) + } services.buildStarted() return services } } + private val log = Logging.getLogger(this.javaClass) - private var startMemory: Long? = null - private val shouldReportMemoryUsage = System.getProperty(SHOULD_REPORT_MEMORY_USAGE_PROPERTY) != null + private var buildHandler: KotlinGradleFinishBuildHandler? = null // There is function with the same name in BuildAdapter, // but it is called before any plugin can attach build listener fun buildStarted() { - startMemory = getUsedMemoryKb() + buildHandler = KotlinGradleFinishBuildHandler() + buildHandler!!.buildStart() TaskLoggers.clear() TaskExecutionResults.clear() @@ -72,57 +103,11 @@ internal class KotlinGradleBuildServices private constructor( } override fun buildFinished(result: BuildResult) { - TaskLoggers.clear() - TaskExecutionResults.clear() - - val gradle = result.gradle!! - GradleCompilerRunner.clearBuildModulesInfo() - - val rootProject = gradle.rootProject - val sessionsDir = GradleCompilerRunner.sessionsDir(rootProject) - if (sessionsDir.exists()) { - val sessionFiles = sessionsDir.listFiles() - - // it is expected that only one session file per build exists - // afaik is is not possible to run multiple gradle builds in one project since gradle locks some dirs - if (sessionFiles.size > 1) { - log.warn("w: Detected multiple Kotlin daemon sessions at ${sessionsDir.relativeToRoot(rootProject)}") - } - for (file in sessionFiles) { - file.delete() - log.kotlinDebug { DELETED_SESSION_FILE_PREFIX + file.relativeToRoot(rootProject) } - } - } - - if (shouldReportMemoryUsage) { - val startMem = startMemory!! - val endMem = getUsedMemoryKb()!! - - // 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.lifecycle("[KOTLIN][PERF] Used memory after build: $endMem kb (difference since build start: ${"%+d".format(endMem - startMem)} kb)") - } - - gradle.removeListener(this) + buildHandler!!.buildFinished(result.gradle!!) instance = null log.kotlinDebug(DISPOSE_MESSAGE) } - private fun getUsedMemoryKb(): Long? { - if (!shouldReportMemoryUsage) return null - - log.lifecycle(FORCE_SYSTEM_GC_MESSAGE) - val gcCountBefore = getGcCount() - System.gc() - while (getGcCount() == gcCountBefore) { - } - - val rt = Runtime.getRuntime() - return (rt.totalMemory() - rt.freeMemory()) / 1024 - } - - private fun getGcCount(): Long = - ManagementFactory.getGarbageCollectorMXBeans().sumByLong { max(0, it.collectionCount) } private val multipleProjectsHolder = KotlinPluginInMultipleProjectsHolder( trackPluginVersionsSeparately = true diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleFinishBuildHandler.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleFinishBuildHandler.kt new file mode 100644 index 00000000000..62c8f26a61d --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleFinishBuildHandler.kt @@ -0,0 +1,81 @@ +/* + * Copyright 2010-2020 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.plugin + +import org.gradle.api.invocation.Gradle +import org.gradle.api.logging.Logging +import org.jetbrains.kotlin.compilerRunner.DELETED_SESSION_FILE_PREFIX +import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner +import org.jetbrains.kotlin.gradle.logging.kotlinDebug +import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskExecutionResults +import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskLoggers +import org.jetbrains.kotlin.gradle.utils.relativeToRoot +import org.jetbrains.kotlin.utils.addToStdlib.sumByLong +import java.lang.management.ManagementFactory +import kotlin.math.max + +// move to KotlinGradleBuildListener when min supported version is 6.1 +class KotlinGradleFinishBuildHandler { + + private val log = Logging.getLogger(this.javaClass) + private var startMemory: Long? = null + private val shouldReportMemoryUsage = System.getProperty(KotlinGradleBuildServices.SHOULD_REPORT_MEMORY_USAGE_PROPERTY) != null + + fun buildStart() { + startMemory = getUsedMemoryKb() + } + + fun buildFinished(gradle: Gradle) { + TaskLoggers.clear() + TaskExecutionResults.clear() + + GradleCompilerRunner.clearBuildModulesInfo() + + val rootProject = gradle.rootProject + val sessionsDir = GradleCompilerRunner.sessionsDir(rootProject) + if (sessionsDir.exists()) { + val sessionFiles = sessionsDir.listFiles() + + // it is expected that only one session file per build exists + // afaik is is not possible to run multiple gradle builds in one project since gradle locks some dirs + if (sessionFiles.size > 1) { + log.warn("w: Detected multiple Kotlin daemon sessions at ${sessionsDir.relativeToRoot(rootProject)}") + } + for (file in sessionFiles) { + file.delete() + log.kotlinDebug { DELETED_SESSION_FILE_PREFIX + file.relativeToRoot(rootProject) } + } + } + + if (shouldReportMemoryUsage) { + val startMem = startMemory!! + val endMem = getUsedMemoryKb()!! + + // 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.lifecycle("[KOTLIN][PERF] Used memory after build: $endMem kb (difference since build start: ${"%+d".format(endMem - startMem)} kb)") + } + + gradle.removeListener(this) + } + + internal fun getUsedMemoryKb(): Long? { + if (!shouldReportMemoryUsage) return null + + log.lifecycle(KotlinGradleBuildServices.FORCE_SYSTEM_GC_MESSAGE) + val gcCountBefore = getGcCount() + System.gc() + while (getGcCount() == gcCountBefore) { + } + + val rt = Runtime.getRuntime() + return (rt.totalMemory() - rt.freeMemory()) / 1024 + } + + private fun getGcCount(): Long = + ManagementFactory.getGarbageCollectorMXBeans().sumByLong { max(0, it.collectionCount) } + +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinMultiplatformPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinMultiplatformPlugin.kt index f076614d565..0f1a5d588cb 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinMultiplatformPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinMultiplatformPlugin.kt @@ -1,3 +1,4 @@ + /* * Copyright 2010-2016 JetBrains s.r.o. * diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt index 39b187d9b2a..5fd7a790fe5 100755 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt @@ -103,7 +103,7 @@ internal abstract class KotlinSourceSetProcessor>( private fun prepareKotlinCompileTask(): TaskProvider = registerKotlinCompileTask(register = ::doRegisterTask).also { task -> - kotlinCompilation.output.addClassesDir { project.files(task.map { it.destinationDir }) } + kotlinCompilation.output.addClassesDir { project.files(task.map { it.destinationDir }).builtBy(task) } } protected fun registerKotlinCompileTask( 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 fefad6cc77c..f6d7f077177 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 @@ -24,6 +24,7 @@ import org.gradle.api.internal.FeaturePreviews import org.gradle.api.internal.file.FileResolver import org.gradle.api.logging.Logger import org.gradle.api.logging.Logging +import org.gradle.build.event.BuildEventsListenerRegistry import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry import org.jetbrains.kotlin.gradle.dsl.* import org.jetbrains.kotlin.gradle.logging.kotlinDebug @@ -49,6 +50,7 @@ import kotlin.reflect.KClass abstract class KotlinBasePluginWrapper( protected val fileResolver: FileResolver ) : Plugin { + private val log = Logging.getLogger(this.javaClass) val kotlinPluginVersion = loadKotlinVersionFromResource(log) @@ -58,7 +60,8 @@ abstract class KotlinBasePluginWrapper( DefaultKotlinSourceSetFactory(project, fileResolver) override fun apply(project: Project) { - val statisticsReporter = KotlinBuildStatsService.getOrCreateInstance(project.gradle) + val listenerRegistryHolder = BuildEventsListenerRegistryHolder.getInstance(project) + val statisticsReporter = KotlinBuildStatsService.getOrCreateInstance(project, listenerRegistryHolder) statisticsReporter?.report(StringMetrics.KOTLIN_COMPILER_VERSION, kotlinPluginVersion) checkGradleCompatibility() @@ -80,7 +83,8 @@ abstract class KotlinBasePluginWrapper( // TODO: consider only set if if daemon or parallel compilation are enabled, though this way it should be safe too System.setProperty(org.jetbrains.kotlin.cli.common.KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY, "true") - val kotlinGradleBuildServices = KotlinGradleBuildServices.getInstance(project.gradle) + + val kotlinGradleBuildServices = KotlinGradleBuildServices.getInstance(project, listenerRegistryHolder) kotlinGradleBuildServices.detectKotlinPluginLoadedInMultipleProjects(project, kotlinPluginVersion) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/gradleUtils.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/gradleUtils.kt index 52ccaa12d46..bc69edcc521 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/gradleUtils.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/gradleUtils.kt @@ -27,16 +27,16 @@ import java.io.File internal fun AbstractCompile.appendClasspathDynamically(file: File) { var added = false - + val objects = project.objects doFirst { if (file !in classpath) { - classpath += project.files(file) + classpath += objects.fileCollection().from(file) added = true } } doLast { if (added) { - classpath -= project.files(file) + classpath -= objects.fileCollection().from(file) } } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/GranularMetadataTransformation.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/GranularMetadataTransformation.kt index ee51f254cbe..3586593d59c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/GranularMetadataTransformation.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/GranularMetadataTransformation.kt @@ -24,9 +24,14 @@ import java.util.zip.ZipOutputStream import javax.xml.parsers.DocumentBuilderFactory internal sealed class MetadataDependencyResolution( + @field:Transient // can't be used with Gradle Instant Execution, but fortunately not needed when deserialized val dependency: ResolvedComponentResult, + @field:Transient val projectDependency: Project? ) { + /** Evaluate and store the value, as the [dependency] will be lost during Gradle instant execution */ +// val originalArtifactFiles: List = dependency.dependents.flatMap { it.allModuleArtifacts } .map { it.file } + override fun toString(): String { val verb = when (this) { is KeepOriginalDependency -> "keep" @@ -59,7 +64,10 @@ internal sealed class MetadataDependencyResolution( * If [doProcessFiles] is true, these temporary files are actually re-created during the call, * otherwise only their paths are returned, while the files might be missing. */ - abstract fun getMetadataFilesBySourceSet(baseDir: File, doProcessFiles: Boolean): Map + fun getMetadataFilesBySourceSet(baseDir: File, doProcessFiles: Boolean): Map = + getExtractableMetadataFiles(baseDir).getMetadataFilesPerSourceSet(doProcessFiles) + + abstract fun getExtractableMetadataFiles(baseDir: File): ExtractableMetadataFiles override fun toString(): String = super.toString() + ", sourceSets = " + allVisibleSourceSetNames.joinToString(", ", "[", "]") { @@ -183,6 +191,7 @@ internal class GranularMetadataTransformation( allModuleDependencies.forEach { resolvedDependency -> if (resolvedDependency.selected !in visitedDependencies) { +// val files = resolvedDependency.moduleArtifacts.map { it.file } result.add( MetadataDependencyResolution.ExcludeAsUnrequested( resolvedDependency.selected, @@ -273,6 +282,20 @@ internal class GranularMetadataTransformation( module, resolvedToProject, projectStructureMetadata, allVisibleSourceSets, visibleSourceSetsExcludingDependsOn, transitiveDependenciesToVisit, mppDependencyMetadataExtractor ) + /* + return object : MetadataDependencyResolution.ChooseVisibleSourceSets( + module, + projectDependency, + projectStructureMetadata, + allVisibleSourceSets, + visibleSourceSetsExcludingDependsOn, + transitiveDependenciesToVisit + ) { + override fun getExtractableMetadataFiles(baseDir: File): ExtractableMetadataFiles = + mppDependencyMetadataExtractor.getExtractableMetadataFiles(visibleSourceSetsExcludingDependsOn, baseDir) + + } + */ } private class ChooseVisibleSourceSetsImpl( @@ -291,18 +314,24 @@ internal class GranularMetadataTransformation( visibleSourceSetNamesExcludingDependsOn, visibleTransitiveDependencies ) { - override fun getMetadataFilesBySourceSet(baseDir: File, doProcessFiles: Boolean): Map = - metadataExtractor.getVisibleSourceSetsMetadata(visibleSourceSetNamesExcludingDependsOn, baseDir, doProcessFiles) + override fun getExtractableMetadataFiles(baseDir: File): ExtractableMetadataFiles = + object : ExtractableMetadataFiles() { + override fun getMetadataFilesPerSourceSet(doProcessFiles: Boolean) : Map + = metadataExtractor.getExtractableMetadataFiles(visibleSourceSetNamesExcludingDependsOn, baseDir, doProcessFiles) + .getMetadataFilesPerSourceSet(doProcessFiles) + } + } } private abstract class MppDependencyMetadataExtractor(val project: Project, val dependency: ResolvedComponentResult) { abstract fun getProjectStructureMetadata(): KotlinProjectStructureMetadata? - abstract fun getVisibleSourceSetsMetadata( + + abstract fun getExtractableMetadataFiles( visibleSourceSetNames: Set, baseDir: File, doProcessFiles: Boolean - ): Map + ): ExtractableMetadataFiles } private class ProjectMppDependencyMetadataExtractor( @@ -313,14 +342,19 @@ private class ProjectMppDependencyMetadataExtractor( override fun getProjectStructureMetadata(): KotlinProjectStructureMetadata? = buildKotlinProjectStructureMetadata(dependencyProject) - override fun getVisibleSourceSetsMetadata( + override fun getExtractableMetadataFiles( visibleSourceSetNames: Set, baseDir: File, doProcessFiles: Boolean - ): Map = - dependencyProject.multiplatformExtension.targets.getByName(KotlinMultiplatformPlugin.METADATA_TARGET_NAME).compilations + ): ExtractableMetadataFiles { + val result = dependencyProject.multiplatformExtension.targets.getByName(KotlinMultiplatformPlugin.METADATA_TARGET_NAME).compilations .filter { it.name in visibleSourceSetNames } .associate { it.defaultSourceSet.name to it.output.classesDirs } + + return object : ExtractableMetadataFiles() { + override fun getMetadataFilesPerSourceSet(doProcessFiles: Boolean): Map = result + } + } } private class JarArtifactMppDependencyMetadataExtractor( @@ -344,72 +378,106 @@ private class JarArtifactMppDependencyMetadataExtractor( } } - override fun getVisibleSourceSetsMetadata( + override fun getExtractableMetadataFiles( visibleSourceSetNames: Set, baseDir: File, doProcessFiles: Boolean - ): Map { + ): ExtractableMetadataFiles { +// val artifactFile = artifact.file val primaryArtifact = primaryArtifact val moduleId = ModuleIds.fromComponent(project, dependency) - return extractSourceSetMetadataFromArtifacts( +// val jarArtifact = artifact +// ?: return object : ExtractableMetadataFiles() { +// override fun getMetadataFilesPerSourceSet(doProcessFiles: Boolean): Map = emptyMap() +// } + + return JarExtractableMetadataFiles( moduleId, + project, + primaryArtifact, +// artifactJar, +// visibleSourceSetNames, baseDir, doProcessFiles, visibleSourceSetNames.associate { it to (metadataArtifactBySourceSet[it] ?: primaryArtifact) } ) } - private fun extractSourceSetMetadataFromArtifacts( - module: ModuleDependencyIdentifier, - baseDir: File, + private class JarExtractableMetadataFiles( + private val module: ModuleDependencyIdentifier, + private val project: Project, + private val primaryArtifact: File, + private val baseDir: File, doProcessFiles: Boolean, - artifactBySourceSet: Map - ): Map { - val moduleString = "${module.groupId}-${module.moduleId}" - val transformedModuleRoot = run { baseDir.resolve(moduleString).also { it.mkdirs() } } + private val artifactBySourceSet: Map + ) : ExtractableMetadataFiles() { - val resultFiles = mutableMapOf() + //TODO its part of extractor. Move it + fun getProjectStructureMetadata(): KotlinProjectStructureMetadata? { + return ZipFile(primaryArtifact).use { zip -> + val metadata = zip.getEntry("META-INF/$MULTIPLATFORM_PROJECT_METADATA_FILE_NAME") + ?: return null - val projectStructureMetadata = checkNotNull(getProjectStructureMetadata()) { - "can't extract metadata from a module without project structure metadata" + val metadataXmlDocument = zip.getInputStream(metadata).use { inputStream -> + DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream) + } + + parseKotlinSourceSetMetadataFromXml(metadataXmlDocument) + } } - artifactBySourceSet.forEach { (sourceSetName, artifact) -> - ZipFile(artifact).use { zip -> - val entries = zip.entries().asSequence().filter { it.name.startsWith("$sourceSetName/") }.toList() + override fun getMetadataFilesPerSourceSet(doProcessFiles: Boolean): Map { + val moduleString = "${module.groupId}-${module.moduleId}" + val transformedModuleRoot = run { baseDir.resolve(moduleString).also { it.mkdirs() } } - // TODO: once IJ supports non-JAR metadata dependencies, extract to a directory, not a JAR - // Also, if both IJ and the CLI compiler can read metadata from a path inside a JAR, then no operations will be needed + val resultFiles = mutableMapOf() + val projectStructureMetadata = checkNotNull(getProjectStructureMetadata()) { + "can't extract metadata from a module without project structure metadata" + } +// val artifactJar :File= artifactBySourceSet.values().first() + artifactBySourceSet.forEach { (sourceSetName, artifact) -> + ZipFile(artifact).use { zip -> + val entries = zip.entries().asSequence().filter { it.name.startsWith("$sourceSetName/") }.toList() - if (entries.any()) { - val extension = projectStructureMetadata.sourceSetBinaryLayout[sourceSetName]?.archiveExtension - ?: SourceSetMetadataLayout.METADATA.archiveExtension + // TODO: once IJ supports non-JAR metadata dependencies, extract to a directory, not a JAR + // Also, if both IJ and the CLI compiler can read metadata from a path inside a JAR, then no operations will be needed - val extractToJarFile = transformedModuleRoot.resolve("$moduleString-$sourceSetName.$extension") - resultFiles[sourceSetName] = project.files(extractToJarFile) + if (entries.any()) { + val extension = projectStructureMetadata.sourceSetBinaryLayout[sourceSetName]?.archiveExtension + ?: SourceSetMetadataLayout.METADATA.archiveExtension - if (doProcessFiles) { - ZipOutputStream(extractToJarFile.outputStream()).use { resultZipOutput -> - for (entry in entries) { - if (entry.isDirectory) - continue + val extractToJarFile = transformedModuleRoot.resolve("$moduleString-$sourceSetName.$extension") + resultFiles[sourceSetName] = project.files(extractToJarFile) - // Drop the source set name from the entry path - val resultEntry = ZipEntry(entry.name.substringAfter("/")) + if (doProcessFiles) { + ZipOutputStream(extractToJarFile.outputStream()).use { resultZipOutput -> + for (entry in entries) { + if (entry.isDirectory) + continue - zip.getInputStream(entry).use { inputStream -> - resultZipOutput.putNextEntry(resultEntry) - inputStream.copyTo(resultZipOutput) - resultZipOutput.closeEntry() + // Drop the source set name from the entry path + val resultEntry = ZipEntry(entry.name.substringAfter("/")) + + zip.getInputStream(entry).use { inputStream -> + resultZipOutput.putNextEntry(resultEntry) + inputStream.copyTo(resultZipOutput) + resultZipOutput.closeEntry() + } } } } } } } - } - return resultFiles + return resultFiles + } } } + +// This class is needed to encapsulate how we extract the files and point to them in a way that doesn't capture the Gradle project state +internal abstract class ExtractableMetadataFiles { + abstract fun getMetadataFilesPerSourceSet(doProcessFiles: Boolean): Map +} + diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/TransformKotlinGranularMetadata.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/TransformKotlinGranularMetadata.kt index ba7d30eb119..824b44a42f5 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/TransformKotlinGranularMetadata.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/TransformKotlinGranularMetadata.kt @@ -15,22 +15,28 @@ import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope.* import org.jetbrains.kotlin.gradle.plugin.sources.getSourceSetHierarchy import org.jetbrains.kotlin.gradle.targets.metadata.ALL_COMPILE_METADATA_CONFIGURATION_NAME import org.jetbrains.kotlin.gradle.targets.metadata.KotlinMetadataTargetConfigurator +import org.jetbrains.kotlin.gradle.utils.getValue import java.io.File import javax.inject.Inject open class TransformKotlinGranularMetadata @Inject constructor( @get:Internal + @field:Transient val kotlinSourceSet: KotlinSourceSet ) : DefaultTask() { @get:OutputDirectory - val outputsDir: File = project.buildDir.resolve("kotlinSourceSetMetadata/${kotlinSourceSet.name}") + val outputsDir: File by project.provider { + project.buildDir.resolve("kotlinSourceSetMetadata/${kotlinSourceSet.name}") + } @Suppress("unused") // Gradle input @get:InputFiles @get:PathSensitive(PathSensitivity.RELATIVE) - internal val allSourceSetsMetadataConfiguration = project.configurations.getByName(ALL_COMPILE_METADATA_CONFIGURATION_NAME) + internal val allSourceSetsMetadataConfiguration: FileCollection by lazy { + project.files(project.configurations.getByName(ALL_COMPILE_METADATA_CONFIGURATION_NAME)) + } private val participatingSourceSets: Set get() = transformation.kotlinSourceSet.getSourceSetHierarchy().toMutableSet().apply { @@ -40,15 +46,14 @@ open class TransformKotlinGranularMetadata @Suppress("unused") // Gradle input @get:Input - internal val inputSourceSetsAndCompilations: Map> - get() { - val sourceSets = participatingSourceSets - return CompilationSourceSetUtil.compilationsBySourceSets(project) - .filterKeys { it in sourceSets } - .entries.associate { (sourceSet, compilations) -> - sourceSet.name to compilations.map { it.name }.sorted() - } + internal val inputSourceSetsAndCompilations: Map> by project.provider { + val sourceSets = participatingSourceSets + CompilationSourceSetUtil.compilationsBySourceSets(project) + .filterKeys { it in sourceSets } + .entries.associate { (sourceSet, compilations) -> + sourceSet.name to compilations.map { it.name }.sorted() } + } private val participatingCompilations: Iterable> get() { @@ -58,13 +63,15 @@ open class TransformKotlinGranularMetadata @Suppress("unused") // Gradle input @get:Input - internal val inputCompilationDependencies: Map>> - get() = participatingCompilations.associate { + internal val inputCompilationDependencies: Map>> by project.provider { + participatingCompilations.associate { it.name to project.configurations.getByName(it.compileDependencyConfigurationName) .allDependencies.map { listOf(it.group, it.name, it.version) }.toSet() } + } @get:Internal + @delegate:Transient internal val transformation: GranularMetadataTransformation by lazy { GranularMetadataTransformation( project, @@ -82,14 +89,39 @@ open class TransformKotlinGranularMetadata } @get:Internal - internal val metadataDependencyResolutions: Iterable - get() = transformation.metadataDependencyResolutions + @delegate:Transient // exclude from Gradle instant execution state + internal val metadataDependencyResolutions: Iterable by project.provider { + transformation.metadataDependencyResolutions + } + //TODO avoid using it @get:Internal internal val filesByResolution: Map get() = metadataDependencyResolutions.filterIsInstance() .associate { it to project.files(it.getMetadataFilesBySourceSet(outputsDir, doProcessFiles = false).values) } +// @get:Internal +// internal val filesByOriginalFiles: Map, FileCollection> by project.provider { +// metadataDependencyResolutions.associate { +// it.originalArtifactFiles to project.files( +// when (it) { +// is MetadataDependencyResolution.ChooseVisibleSourceSets -> +// it.getMetadataFilesBySourceSet(outputsDir, doProcessFiles = false).values +// is MetadataDependencyResolution.ExcludeAsUnrequested -> +// emptyList() +// is MetadataDependencyResolution.KeepOriginalDependency -> +// it.originalArtifactFiles +// } +// ) +// } +// } + + private val extractableFiles by project.provider { + transformation.metadataDependencyResolutions + .filterIsInstance() + .map { it.getExtractableMetadataFiles(outputsDir) } + } + @TaskAction fun transformMetadata() { if (outputsDir.isDirectory) { @@ -97,8 +129,6 @@ open class TransformKotlinGranularMetadata } outputsDir.mkdirs() - metadataDependencyResolutions - .filterIsInstance() - .forEach { it.getMetadataFilesBySourceSet(outputsDir, doProcessFiles = true) } + extractableFiles.forEach { it.getMetadataFilesPerSourceSet(doProcessFiles = true) } } } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/DefaultLanguageSettingsBuilder.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/DefaultLanguageSettingsBuilder.kt index 5e4455f2d8e..5512d238ea8 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/DefaultLanguageSettingsBuilder.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/DefaultLanguageSettingsBuilder.kt @@ -69,6 +69,7 @@ internal class DefaultLanguageSettingsBuilder : LanguageSettingsBuilder { } /* A Kotlin task that is responsible for code analysis of the owner of this language settings builder. */ + @Transient // not needed during Gradle Instant Execution var compilerPluginOptionsTask: Lazy = lazyOf(null) val compilerPluginArguments: List? @@ -76,7 +77,7 @@ internal class DefaultLanguageSettingsBuilder : LanguageSettingsBuilder { val pluginOptionsTask = compilerPluginOptionsTask.value ?: return null return when (pluginOptionsTask) { is AbstractKotlinCompile<*> -> pluginOptionsTask.pluginOptions - is AbstractKotlinNativeCompile<*> -> pluginOptionsTask.compilerPluginOptions + is AbstractKotlinNativeCompile<*, *> -> pluginOptionsTask.compilerPluginOptions else -> error("Unexpected task: $pluginOptionsTask") }.arguments } @@ -86,7 +87,7 @@ internal class DefaultLanguageSettingsBuilder : LanguageSettingsBuilder { val pluginClasspathTask = compilerPluginOptionsTask.value ?: return null return when (pluginClasspathTask) { is AbstractKotlinCompile<*> -> pluginClasspathTask.pluginClasspath - is AbstractKotlinNativeCompile<*> -> pluginClasspathTask.compilerPluginClasspath ?: pluginClasspathTask.project.files() + is AbstractKotlinNativeCompile<*, *> -> pluginClasspathTask.compilerPluginClasspath ?: pluginClasspathTask.project.files() else -> error("Unexpected task: $pluginClasspathTask") } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/KotlinBuildStatHandler.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/KotlinBuildStatHandler.kt new file mode 100644 index 00000000000..abace0dbfaa --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/KotlinBuildStatHandler.kt @@ -0,0 +1,215 @@ +/* + * Copyright 2010-2020 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.plugin.statistics + +import org.gradle.api.Project +import org.gradle.api.artifacts.DependencySet +import org.gradle.api.invocation.Gradle +import org.gradle.api.logging.Logging +import org.jetbrains.kotlin.gradle.utils.API +import org.jetbrains.kotlin.gradle.utils.COMPILE +import org.jetbrains.kotlin.gradle.utils.IMPLEMENTATION +import org.jetbrains.kotlin.gradle.utils.RUNTIME +import org.jetbrains.kotlin.statistics.BuildSessionLogger +import org.jetbrains.kotlin.statistics.metrics.BooleanMetrics +import org.jetbrains.kotlin.statistics.metrics.NumericalMetrics +import org.jetbrains.kotlin.statistics.metrics.StringMetrics +import java.io.File +import java.lang.management.ManagementFactory +import javax.management.MBeanServer +import javax.management.ObjectName +import kotlin.system.measureTimeMillis + +class KotlinBuildStatHandler { + companion object { + @JvmStatic + internal fun getLogger() = Logging.getLogger(KotlinBuildStatHandler::class.java) + + internal fun runSafe(methodName: String, action: () -> T?): T? { + return try { + getLogger().debug("Executing [$methodName]") + action.invoke() + } catch (e: Throwable) { + logException("Could not execute [$methodName]", e) + null + } + } + + internal fun logException(description: String, e: Throwable) { + getLogger().info(description) + getLogger().debug(e.message, e) + } + + } + + fun buildFinished( + gradle: Gradle?, + beanName: ObjectName, + sessionLogger: BuildSessionLogger, + action: String?, + failure: Throwable? + ) { + runSafe("${KotlinBuildStatHandler::class.java}.buildFinished") { + try { + try { + if (gradle != null) reportGlobalMetrics(gradle, sessionLogger) + } finally { + sessionLogger.finishBuildSession(action, failure) + } + } finally { + val mbs: MBeanServer = ManagementFactory.getPlatformMBeanServer() + if (mbs.isRegistered(beanName)) { + mbs.unregisterMBean(beanName) + } + } + } + } + + internal fun reportGlobalMetrics(gradle: Gradle, sessionLogger: BuildSessionLogger) { + System.getProperty("os.name")?.also { + sessionLogger.report(StringMetrics.OS_TYPE, System.getProperty("os.name")) + } + sessionLogger.report(NumericalMetrics.CPU_NUMBER_OF_CORES, Runtime.getRuntime().availableProcessors().toLong()) + sessionLogger.report(StringMetrics.GRADLE_VERSION, gradle.gradleVersion) + sessionLogger.report(BooleanMetrics.EXECUTED_FROM_IDEA, System.getProperty("idea.active") != null) + sessionLogger.report(NumericalMetrics.GRADLE_DAEMON_HEAP_SIZE, Runtime.getRuntime().maxMemory()) + sessionLogger.report( + BooleanMetrics.KOTLIN_OFFICIAL_CODESTYLE, + gradle.rootProject.properties["kotlin.code.style"] == "official" + ) // constants are saved in IDEA plugin and could not be accessed directly + + gradle.taskGraph.whenReady() { taskExecutionGraph -> + val executedTaskNames = taskExecutionGraph.allTasks.map { it.name }.distinct() + report(sessionLogger, BooleanMetrics.COMPILATION_STARTED, executedTaskNames.contains("compileKotlin"), null) + report(sessionLogger, BooleanMetrics.TESTS_EXECUTED, executedTaskNames.contains("compileTestKotlin"), null) + report(sessionLogger, BooleanMetrics.MAVEN_PUBLISH_EXECUTED, executedTaskNames.contains("install"), null) + } + + fun buildSrcExists(project: Project) = File(project.projectDir, "buildSrc").exists() + if (buildSrcExists(gradle.rootProject)) { + sessionLogger.report(BooleanMetrics.BUILD_SRC_EXISTS, true) + } + val statisticOverhead = measureTimeMillis { + gradle.allprojects { project -> + for (configuration in project.configurations) { + val configurationName = configuration.name + val dependencies = configuration.dependencies + + when (configurationName) { + "kapt" -> { + sessionLogger.report(BooleanMetrics.ENABLED_KAPT, true) + dependencies?.forEach { dependency -> + when (dependency.group) { + "com.google.dagger" -> sessionLogger.report(BooleanMetrics.ENABLED_DAGGER, true) + "com.android.databinding" -> sessionLogger.report(BooleanMetrics.ENABLED_DATABINDING, true) + } + } + } + API -> { + sessionLogger.report(NumericalMetrics.CONFIGURATION_API_COUNT, 1) + reportLibrariesVersions(sessionLogger, dependencies) + } + IMPLEMENTATION -> { + sessionLogger.report(NumericalMetrics.CONFIGURATION_IMPLEMENTATION_COUNT, 1) + reportLibrariesVersions(sessionLogger, dependencies) + } + COMPILE -> { + sessionLogger.report(NumericalMetrics.CONFIGURATION_COMPILE_COUNT, 1) + reportLibrariesVersions(sessionLogger, dependencies) + } + RUNTIME -> { + sessionLogger.report(NumericalMetrics.CONFIGURATION_RUNTIME_COUNT, 1) + reportLibrariesVersions(sessionLogger, dependencies) + } + } + } + + sessionLogger.report(NumericalMetrics.NUMBER_OF_SUBPROJECTS, 1) + sessionLogger.report(BooleanMetrics.KOTLIN_KTS_USED, project.buildscript.sourceFile?.name?.endsWith(".kts") ?: false) + sessionLogger.report(NumericalMetrics.GRADLE_NUMBER_OF_TASKS, project.tasks.names.size.toLong()) + sessionLogger.report( + NumericalMetrics.GRADLE_NUMBER_OF_UNCONFIGURED_TASKS, + project.tasks.names.count { name -> + try { + project.tasks.named(name).javaClass.name.contains("TaskCreatingProvider") + } catch (_: Exception) { + true + } + }.toLong() + ) + + if (buildSrcExists(project)) { + sessionLogger.report(NumericalMetrics.BUILD_SRC_COUNT, 1) + sessionLogger.report(BooleanMetrics.BUILD_SRC_EXISTS, true) + } + } + } + sessionLogger.report(NumericalMetrics.STATISTICS_VISIT_ALL_PROJECTS_OVERHEAD, statisticOverhead) + } + + private fun reportLibrariesVersions(sessionLogger: BuildSessionLogger, dependencies: DependencySet?) { + dependencies?.forEach { dependency -> + when { + dependency.group?.startsWith("org.springframework") ?: false -> sessionLogger.report( + StringMetrics.LIBRARY_SPRING_VERSION, + dependency.version ?: "0.0.0" + ) + dependency.group?.startsWith("com.vaadin") ?: false -> sessionLogger.report( + StringMetrics.LIBRARY_VAADIN_VERSION, + dependency.version ?: "0.0.0" + ) + dependency.group?.startsWith("com.google.gwt") ?: false -> sessionLogger.report( + StringMetrics.LIBRARY_GWT_VERSION, + dependency.version ?: "0.0.0" + ) + dependency.group?.startsWith("org.hibernate") ?: false -> sessionLogger.report( + StringMetrics.LIBRARY_HIBERNATE_VERSION, + dependency.version ?: "0.0.0" + ) + dependency.group == "org.jetbrains.kotlin" && dependency.name.startsWith("kotlin-stdlib") -> sessionLogger.report( + StringMetrics.KOTLIN_STDLIB_VERSION, + dependency.version ?: "0.0.0" + ) + dependency.group == "org.jetbrains.kotlinx" && dependency.name == "kotlinx-coroutines" -> sessionLogger.report( + StringMetrics.KOTLIN_COROUTINES_VERSION, + dependency.version ?: "0.0.0" + ) + dependency.group == "org.jetbrains.kotlin" && dependency.name == "kotlin-reflect" -> sessionLogger.report( + StringMetrics.KOTLIN_REFLECT_VERSION, + dependency.version ?: "0.0.0" + ) + dependency.group == "org.jetbrains.kotlinx" && dependency.name + .startsWith("kotlinx-serialization-runtime") -> sessionLogger.report( + StringMetrics.KOTLIN_SERIALIZATION_VERSION, + dependency.version ?: "0.0.0" + ) + dependency.group == "com.android.tools.build" && dependency.name.startsWith("gradle") -> sessionLogger.report( + StringMetrics.ANDROID_GRADLE_PLUGIN_VERSION, + dependency.version ?: "0.0.0" + ) + } + } + } + + internal fun report(sessionLogger: BuildSessionLogger, metric: BooleanMetrics, value: Boolean, subprojectName: String?) { + runSafe("report metric ${metric.name}") { + sessionLogger.report(metric, value, subprojectName) + } + + } + + internal fun report(sessionLogger: BuildSessionLogger, metric: NumericalMetrics, value: Long, subprojectName: String?) { + runSafe("report metric ${metric.name}") { + sessionLogger.report(metric, value, subprojectName) + } + } + + internal fun report(sessionLogger: BuildSessionLogger, metric: StringMetrics, value: String, subprojectName: String?) { + runSafe("report metric ${metric.name}") { + sessionLogger.report(metric, value, subprojectName) + } + } +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/KotlinBuildStatListener.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/KotlinBuildStatListener.kt new file mode 100644 index 00000000000..862560aaf3e --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/KotlinBuildStatListener.kt @@ -0,0 +1,66 @@ +/* + * Copyright 2010-2020 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.plugin.statistics + +import org.gradle.api.invocation.Gradle +import org.gradle.tooling.events.FinishEvent +import org.gradle.tooling.events.OperationCompletionListener +import org.gradle.tooling.events.configuration.ProjectConfigurationFinishEvent +import org.jetbrains.kotlin.statistics.BuildSessionLogger +import java.lang.management.ManagementFactory +import javax.management.MBeanServer +import javax.management.ObjectName + +open class KotlinBuildStatListener(val beanName: ObjectName/*, val gradle: Gradle*/) : OperationCompletionListener, AutoCloseable { + + private var projectEvaluatedTime: Long? = null + + override fun onFinish(event: FinishEvent?) { + if (event is ProjectConfigurationFinishEvent) { + projectEvaluatedTime = event.eventTime + } + //todo is it any chance to get failure exception? + //todo nothing to do? +// KotlinBuildStatHandler.runSafe("${KotlinBuildStatListener::class.java}.onFinish") { +// +// +// try { +// val finishTime = event?.result?.endTime +// val startTime = event?.result?.startTime +// report(NumericalMetrics.GRADLE_BUILD_DURATION, finishTime - it.buildStartedTime) +// report(NumericalMetrics.GRADLE_EXECUTION_DURATION, finishTime - it.projectEvaluatedTime) +// report(NumericalMetrics.BUILD_FINISH_TIME, finishTime) +// } finally { +// val mbs: MBeanServer = ManagementFactory.getPlatformMBeanServer() +// if (mbs.isRegistered(beanName)) { +// mbs.unregisterMBean(beanName) +// } +// } +// } + + } + + override fun close() { +// val sessionLogger = BuildSessionLogger(gradle.gradleUserHomeDir) +// KotlinBuildStatHandler.runSafe("${KotlinBuildStatListener::class.java}.close()") { +// try { +// try { +// KotlinBuildStatHandler().reportGlobalMetrics(gradle, sessionLogger) +// } finally { +//// report(NumericalMetrics.GRADLE_BUILD_DURATION, finishTime - it.buildStartedTime) +//// report(NumericalMetrics.GRADLE_EXECUTION_DURATION, finishTime - it.projectEvaluatedTime) +//// report(NumericalMetrics.BUILD_FINISH_TIME, finishTime) +// } +// +// } finally { +// val mbs: MBeanServer = ManagementFactory.getPlatformMBeanServer() +// if (mbs.isRegistered(beanName)) { +// mbs.unregisterMBean(beanName) +// } +// } +// } + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/KotlinBuildStatsService.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/KotlinBuildStatsService.kt index 8bd3f7dc272..b8981310215 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/KotlinBuildStatsService.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/KotlinBuildStatsService.kt @@ -8,15 +8,13 @@ package org.jetbrains.kotlin.gradle.plugin.statistics import org.gradle.BuildAdapter import org.gradle.BuildResult import org.gradle.api.Project -import org.gradle.api.artifacts.DependencySet import org.gradle.api.invocation.Gradle import org.gradle.api.logging.Logging import org.gradle.initialization.BuildRequestMetaData import org.gradle.invocation.DefaultGradle -import org.jetbrains.kotlin.gradle.utils.API -import org.jetbrains.kotlin.gradle.utils.COMPILE -import org.jetbrains.kotlin.gradle.utils.IMPLEMENTATION -import org.jetbrains.kotlin.gradle.utils.RUNTIME +import org.jetbrains.kotlin.gradle.plugin.BuildEventsListenerRegistryHolder +import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatHandler.Companion.runSafe +import org.jetbrains.kotlin.gradle.utils.isConfigurationCacheAvailable import org.jetbrains.kotlin.statistics.BuildSessionLogger import org.jetbrains.kotlin.statistics.BuildSessionLogger.Companion.STATISTICS_FOLDER_NAME import org.jetbrains.kotlin.statistics.metrics.BooleanMetrics @@ -87,8 +85,10 @@ internal abstract class KotlinBuildStatsService internal constructor() : BuildAd */ @JvmStatic @Synchronized - internal fun getOrCreateInstance(gradle: Gradle): IStatisticsValuesConsumer? { + internal fun getOrCreateInstance(project: Project, listenerRegistryHolder: BuildEventsListenerRegistryHolder): IStatisticsValuesConsumer? { + return runSafe("${KotlinBuildStatsService::class.java}.getOrCreateInstance") { + val gradle = project.gradle statisticsIsEnabled = statisticsIsEnabled ?: checkStatisticsEnabled(gradle) if (statisticsIsEnabled != true) { null @@ -106,13 +106,17 @@ internal abstract class KotlinBuildStatsService internal constructor() : BuildAd ) instance = JMXKotlinBuildStatsService(mbs, beanName) } else { + val kotlinBuildStatProvider = project.provider{ KotlinBuildStatListener(beanName) } val newInstance = DefaultKotlinBuildStatsService(gradle, beanName) + instance = newInstance log.debug("Instantiated ${KotlinBuildStatsService::class.java}: new instance $instance") mbs.registerMBean(StandardMBean(newInstance, KotlinBuildStatsMXBean::class.java), beanName) } } - gradle.addBuildListener(instance) + if (!isConfigurationCacheAvailable(gradle)) { + gradle.addBuildListener(instance) + } instance } } @@ -130,7 +134,7 @@ internal abstract class KotlinBuildStatsService internal constructor() : BuildAd } this.report(NumericalMetrics.STATISTICS_COLLECT_METRICS_OVERHEAD, duration) } catch (e: Throwable) { - logException("Could collect statistics metrics", e) + KotlinBuildStatHandler.logException("Could collect statistics metrics", e) } } } @@ -149,21 +153,6 @@ internal abstract class KotlinBuildStatsService internal constructor() : BuildAd gradle.rootProject.properties[ENABLE_STATISTICS_PROPERTY_NAME]?.toString()?.toBoolean() ?: DEFAULT_STATISTICS_STATE } } - - private fun logException(description: String, e: Throwable) { - getLogger().info(description) - getLogger().debug(e.message, e) - } - - internal fun runSafe(methodName: String, action: () -> T?): T? { - return try { - getLogger().debug("Executing [$methodName]") - action.invoke() - } catch (e: Throwable) { - logException("Could not execute [$methodName]", e) - null - } - } } } @@ -212,132 +201,6 @@ internal class DefaultKotlinBuildStatsService internal constructor( return (gradle as? DefaultGradle)?.services?.get(BuildRequestMetaData::class.java)?.startTime } - private fun reportLibrariesVersions(dependencies: DependencySet?) { - dependencies?.forEach { dependency -> - when { - dependency.group?.startsWith("org.springframework") ?: false -> sessionLogger.report( - StringMetrics.LIBRARY_SPRING_VERSION, - dependency.version ?: "0.0.0" - ) - dependency.group?.startsWith("com.vaadin") ?: false -> sessionLogger.report( - StringMetrics.LIBRARY_VAADIN_VERSION, - dependency.version ?: "0.0.0" - ) - dependency.group?.startsWith("com.google.gwt") ?: false -> sessionLogger.report( - StringMetrics.LIBRARY_GWT_VERSION, - dependency.version ?: "0.0.0" - ) - dependency.group?.startsWith("org.hibernate") ?: false -> sessionLogger.report( - StringMetrics.LIBRARY_HIBERNATE_VERSION, - dependency.version ?: "0.0.0" - ) - dependency.group == "org.jetbrains.kotlin" && dependency.name.startsWith("kotlin-stdlib") -> sessionLogger.report( - StringMetrics.KOTLIN_STDLIB_VERSION, - dependency.version ?: "0.0.0" - ) - dependency.group == "org.jetbrains.kotlinx" && dependency.name == "kotlinx-coroutines" -> sessionLogger.report( - StringMetrics.KOTLIN_COROUTINES_VERSION, - dependency.version ?: "0.0.0" - ) - dependency.group == "org.jetbrains.kotlin" && dependency.name == "kotlin-reflect" -> sessionLogger.report( - StringMetrics.KOTLIN_REFLECT_VERSION, - dependency.version ?: "0.0.0" - ) - dependency.group == "org.jetbrains.kotlinx" && dependency.name - .startsWith("kotlinx-serialization-runtime") -> sessionLogger.report( - StringMetrics.KOTLIN_SERIALIZATION_VERSION, - dependency.version ?: "0.0.0" - ) - dependency.group == "com.android.tools.build" && dependency.name.startsWith("gradle") -> sessionLogger.report( - StringMetrics.ANDROID_GRADLE_PLUGIN_VERSION, - dependency.version ?: "0.0.0" - ) - } - } - } - - private fun reportGlobalMetrics(gradle: Gradle) { - System.getProperty("os.name")?.also { - sessionLogger.report(StringMetrics.OS_TYPE, System.getProperty("os.name")) - } - sessionLogger.report(NumericalMetrics.CPU_NUMBER_OF_CORES, Runtime.getRuntime().availableProcessors().toLong()) - sessionLogger.report(StringMetrics.GRADLE_VERSION, gradle.gradleVersion) - sessionLogger.report(BooleanMetrics.EXECUTED_FROM_IDEA, System.getProperty("idea.active") != null) - sessionLogger.report(NumericalMetrics.GRADLE_DAEMON_HEAP_SIZE, Runtime.getRuntime().maxMemory()) - sessionLogger.report( - BooleanMetrics.KOTLIN_OFFICIAL_CODESTYLE, - gradle.rootProject.properties["kotlin.code.style"] == "official" - ) // constants are saved in IDEA plugin and could not be accessed directly - - gradle.taskGraph.whenReady() { taskExecutionGraph -> - val executedTaskNames = taskExecutionGraph.allTasks.map { it.name }.distinct() - report(BooleanMetrics.COMPILATION_STARTED, executedTaskNames.contains("compileKotlin")) - report(BooleanMetrics.TESTS_EXECUTED, executedTaskNames.contains("compileTestKotlin")) - report(BooleanMetrics.MAVEN_PUBLISH_EXECUTED, executedTaskNames.contains("install")) - } - - fun buildSrcExists(project: Project) = File(project.projectDir, "buildSrc").exists() - if (buildSrcExists(gradle.rootProject)) { - sessionLogger.report(BooleanMetrics.BUILD_SRC_EXISTS, true) - } - val statisticOverhead = measureTimeMillis { - gradle.allprojects { project -> - for (configuration in project.configurations) { - val configurationName = configuration.name - val dependencies = configuration.dependencies - - when (configurationName) { - "kapt" -> { - sessionLogger.report(BooleanMetrics.ENABLED_KAPT, true) - dependencies?.forEach { dependency -> - when (dependency.group) { - "com.google.dagger" -> sessionLogger.report(BooleanMetrics.ENABLED_DAGGER, true) - "com.android.databinding" -> sessionLogger.report(BooleanMetrics.ENABLED_DATABINDING, true) - } - } - } - API -> { - sessionLogger.report(NumericalMetrics.CONFIGURATION_API_COUNT, 1) - reportLibrariesVersions(dependencies) - } - IMPLEMENTATION -> { - sessionLogger.report(NumericalMetrics.CONFIGURATION_IMPLEMENTATION_COUNT, 1) - reportLibrariesVersions(dependencies) - } - COMPILE -> { - sessionLogger.report(NumericalMetrics.CONFIGURATION_COMPILE_COUNT, 1) - reportLibrariesVersions(dependencies) - } - RUNTIME -> { - sessionLogger.report(NumericalMetrics.CONFIGURATION_RUNTIME_COUNT, 1) - reportLibrariesVersions(dependencies) - } - } - } - - sessionLogger.report(NumericalMetrics.NUMBER_OF_SUBPROJECTS, 1) - sessionLogger.report(BooleanMetrics.KOTLIN_KTS_USED, project.buildscript.sourceFile?.name?.endsWith(".kts") ?: false) - sessionLogger.report(NumericalMetrics.GRADLE_NUMBER_OF_TASKS, project.tasks.names.size.toLong()) - sessionLogger.report( - NumericalMetrics.GRADLE_NUMBER_OF_UNCONFIGURED_TASKS, - project.tasks.names.count { name -> - try { - project.tasks.named(name).javaClass.name.contains("TaskCreatingProvider") - } catch (_: Exception) { - true - } - }.toLong() - ) - - if (buildSrcExists(project)) { - sessionLogger.report(NumericalMetrics.BUILD_SRC_COUNT, 1) - sessionLogger.report(BooleanMetrics.BUILD_SRC_EXISTS, true) - } - } - } - sessionLogger.report(NumericalMetrics.STATISTICS_VISIT_ALL_PROJECTS_OVERHEAD, statisticOverhead) - } - override fun projectsEvaluated(gradle: Gradle) { runSafe("${DefaultKotlinBuildStatsService::class.java}.projectEvaluated") { if (!sessionLogger.isBuildSessionStarted()) { @@ -351,41 +214,20 @@ internal class DefaultKotlinBuildStatsService internal constructor( @Synchronized override fun buildFinished(result: BuildResult) { - runSafe("${DefaultKotlinBuildStatsService::class.java}.buildFinished") { - try { - try { - val gradle = result.gradle - if (gradle != null) reportGlobalMetrics(gradle) - } finally { - sessionLogger.finishBuildSession(result.action, result.failure) - } - } finally { - val mbs: MBeanServer = ManagementFactory.getPlatformMBeanServer() - if (mbs.isRegistered(beanName)) { - mbs.unregisterMBean(beanName) - } - instance = null - } - } + KotlinBuildStatHandler().buildFinished(result.gradle, beanName, sessionLogger, result.action, result.failure) + instance = null } override fun report(metric: BooleanMetrics, value: Boolean, subprojectName: String?) { - runSafe("report metric ${metric.name}") { - sessionLogger.report(metric, value, subprojectName) - } - + KotlinBuildStatHandler().report(sessionLogger, metric, value, subprojectName) } override fun report(metric: NumericalMetrics, value: Long, subprojectName: String?) { - runSafe("report metric ${metric.name}") { - sessionLogger.report(metric, value, subprojectName) - } + KotlinBuildStatHandler().report(sessionLogger, metric, value, subprojectName) } override fun report(metric: StringMetrics, value: String, subprojectName: String?) { - runSafe("report metric ${metric.name}") { - sessionLogger.report(metric, value, subprojectName) - } + KotlinBuildStatHandler().report(sessionLogger, metric, value, subprojectName) } override fun reportBoolean(name: String, value: Boolean, subprojectName: String?) { diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/report/KotlinBuildReporter.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/report/KotlinBuildReporter.kt index c6a390f9096..c819d6668ed 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/report/KotlinBuildReporter.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/report/KotlinBuildReporter.kt @@ -16,11 +16,10 @@ import org.jetbrains.kotlin.gradle.logging.kotlinDebug import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskExecutionResults import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile +import org.jetbrains.kotlin.gradle.utils.isConfigurationCacheAvailable import java.io.File -import java.lang.StringBuilder import java.text.SimpleDateFormat import java.util.* -import kotlin.math.max internal fun configureBuildReporter(gradle: Gradle, log: Logger) { val rootProject = gradle.rootProject @@ -40,8 +39,11 @@ internal fun configureBuildReporter(gradle: Gradle, log: Logger) { val ts = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(Calendar.getInstance().time) val perfReportFile = perfLogDir.resolve("${gradle.rootProject.name}-build-$ts.txt") - val reporter = KotlinBuildReporter(gradle, perfReportFile) - gradle.addBuildListener(reporter) + + if (!isConfigurationCacheAvailable(gradle)) { + val reporter = KotlinBuildReporter(gradle, perfReportFile) + gradle.addBuildListener(reporter) + } val buildReportMode = if (properties.buildReportVerbose) BuildReportMode.VERBOSE else BuildReportMode.SIMPLE @@ -112,94 +114,7 @@ internal class KotlinBuildReporter( @Synchronized override fun buildFinished(result: BuildResult) { - val logger = result.gradle?.rootProject?.logger - try { - perfReportFile.writeText(buildInfo(result) + taskOverview() + tasksSb.toString()) - logger?.lifecycle("Kotlin build report is written to ${perfReportFile.canonicalPath}") - } catch (e: Throwable) { - logger?.error("Could not write Kotlin build report to ${perfReportFile.canonicalPath}", e) - } - } - - private fun buildInfo(result: BuildResult): String { - val startParams = arrayListOf() - gradle.startParameter.apply { - startParams.add("tasks = ${taskRequests.joinToString { it.args.toString() }}") - startParams.add("excluded tasks = $excludedTaskNames") - startParams.add("current dir = $currentDir") - startParams.add("project properties args = $projectProperties") - startParams.add("system properties args = $systemPropertiesArgs") - } - - return buildString { - appendln("Gradle start parameters:") - startParams.forEach { - appendln(" $it") - } - if (result.failure != null) { - appendln("Build failed: ${result.failure}") - } - appendln() - } - } - - private fun taskOverview(): String { - if (kotlinTaskTimeNs.isEmpty()) return buildString { appendln("No Kotlin task was run") } - - val sb = StringBuilder() - val kotlinTotalTimeNs = kotlinTaskTimeNs.values.sum() - val ktTaskPercent = (kotlinTotalTimeNs.toDouble() / allTasksTimeNs * 100).asString(1) - - sb.appendln("Total time for Kotlin tasks: ${formatTime(kotlinTotalTimeNs)} ($ktTaskPercent % of all tasks time)") - - val table = TextTable("Time", "% of Kotlin time", "Task") - kotlinTaskTimeNs.entries - .sortedByDescending { (_, timeNs) -> timeNs } - .forEach { (task, timeNs) -> - val percent = (timeNs.toDouble() / kotlinTotalTimeNs * 100).asString(1) - table.addRow(formatTime(timeNs), "$percent %", task.path) - } - table.printTo(sb) - sb.appendln() - return sb.toString() - } - - private fun formatTime(ns: Long): String { - val seconds = ns.toDouble() / 1_000_000_000 - return seconds.asString(2) + " s" - } - - private fun Double.asString(decPoints: Int): String = - String.format("%.${decPoints}f", this) - - private class TextTable(vararg columnNames: String) { - private val rows = ArrayList>() - private val columnsCount = columnNames.size - private val maxLengths = IntArray(columnsCount) { columnNames[it].length } - - init { - rows.add(columnNames.toList()) - } - - fun addRow(vararg row: String) { - check(row.size == columnsCount) { "Row size ${row.size} differs from columns count $columnsCount" } - rows.add(row.toList()) - - for ((i, col) in row.withIndex()) { - maxLengths[i] = max(maxLengths[i], col.length) - } - } - - fun printTo(sb: StringBuilder) { - for (row in rows) { - sb.appendln() - for ((i, col) in row.withIndex()) { - if (i > 0) sb.append("|") - - sb.append(col.padEnd(maxLengths[i], ' ')) - } - } - } + KotlinBuildReporterHandler().buildFinished(gradle, perfReportFile, kotlinTaskTimeNs.mapKeys{it.key.path}, allTasksTimeNs, result.failure) } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/report/KotlinBuildReporterHandler.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/report/KotlinBuildReporterHandler.kt new file mode 100644 index 00000000000..a3b3a948cb4 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/report/KotlinBuildReporterHandler.kt @@ -0,0 +1,110 @@ +/* + * Copyright 2010-2020 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.report + +import org.gradle.api.invocation.Gradle +import java.io.File +import java.util.* +import kotlin.math.max + +class KotlinBuildReporterHandler { + fun buildFinished( + gradle: Gradle, + perfReportFile: File, + kotlinTaskTimeNs: Map, + allTasksTimeNs: Long, + failure: Throwable? = null + ) { + val logger = gradle.rootProject.logger + try { + perfReportFile.writeText(buildInfo(gradle, failure) + taskOverview(kotlinTaskTimeNs, allTasksTimeNs)) + logger.lifecycle("Kotlin build report is written to ${perfReportFile.canonicalPath}") + } catch (e: Throwable) { + logger.error("Could not write Kotlin build report to ${perfReportFile.canonicalPath}", e) + } + } + + private fun buildInfo(gradle: Gradle, failure: Throwable?): String { + val startParams = arrayListOf() + gradle.startParameter.apply { + startParams.add("tasks = ${taskRequests.joinToString { it.args.toString() }}") + startParams.add("excluded tasks = $excludedTaskNames") + startParams.add("current dir = $currentDir") + startParams.add("project properties args = $projectProperties") + startParams.add("system properties args = $systemPropertiesArgs") + } + + return buildString { + appendln("Gradle start parameters:") + startParams.forEach { + appendln(" $it") + } + if (failure != null) { + appendln("Build failed: ${failure}") + } + appendln() + } + } + + internal fun taskOverview(kotlinTaskTimeNs: Map, allTasksTimeNs: Long): String { + if (kotlinTaskTimeNs.isEmpty()) return buildString { appendln("No Kotlin task was run") } + + val sb = StringBuilder() + val kotlinTotalTimeNs = kotlinTaskTimeNs.values.sum() + val ktTaskPercent = (kotlinTotalTimeNs.toDouble() / allTasksTimeNs * 100).asString(1) + + sb.appendln("Total time for Kotlin tasks: ${formatTime(kotlinTotalTimeNs)} ($ktTaskPercent % of all tasks time)") + + val table = TextTable("Time", "% of Kotlin time", "Task") + kotlinTaskTimeNs.entries + .sortedByDescending { (_, timeNs) -> timeNs } + .forEach { (taskPath, timeNs) -> + val percent = (timeNs.toDouble() / kotlinTotalTimeNs * 100).asString(1) + table.addRow(formatTime(timeNs), "$percent %", taskPath) + } + table.printTo(sb) + sb.appendln() + return sb.toString() + } + + internal class TextTable(vararg columnNames: String) { + private val rows = ArrayList>() + private val columnsCount = columnNames.size + private val maxLengths = IntArray(columnsCount) { columnNames[it].length } + + init { + rows.add(columnNames.toList()) + } + + fun addRow(vararg row: String) { + check(row.size == columnsCount) { "Row size ${row.size} differs from columns count $columnsCount" } + rows.add(row.toList()) + + for ((i, col) in row.withIndex()) { + maxLengths[i] = max(maxLengths[i], col.length) + } + } + + fun printTo(sb: StringBuilder) { + for (row in rows) { + sb.appendln() + for ((i, col) in row.withIndex()) { + if (i > 0) sb.append("|") + + sb.append(col.padEnd(maxLengths[i], ' ')) + } + } + } + } +} + +internal fun formatTime(ns: Long): String { + val seconds = ns.toDouble() / 1_000_000_000 + return seconds.asString(2) + " s" +} + +internal fun Double.asString(decPoints: Int): String = + String.format("%.${decPoints}f", this) \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/report/KotlinBuildReporterListener.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/report/KotlinBuildReporterListener.kt new file mode 100644 index 00000000000..6d95799f3f9 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/report/KotlinBuildReporterListener.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2010-2020 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.report + +import com.intellij.util.text.DateFormatUtil.formatTime +import org.gradle.api.invocation.Gradle +import org.gradle.tooling.events.FinishEvent +import org.gradle.tooling.events.OperationCompletionListener +import org.gradle.tooling.events.task.TaskOperationDescriptor +import java.io.File +import java.util.* + +class KotlinBuildReporterListener(val gradle: Gradle, val perfReportFile: File) : OperationCompletionListener, AutoCloseable { + + private val kotlinTaskTimeNs = HashMap() + internal var allTasksTimeNs: Long = 0L + + override fun onFinish(event: FinishEvent?) { + val descriptor = event?.descriptor + if (descriptor is TaskOperationDescriptor) { + val taskPath = descriptor.taskPath + val executionTime = event.result.endTime - event.result.startTime + allTasksTimeNs += executionTime + kotlinTaskTimeNs[taskPath] = executionTime + } + } + + override fun close() { + KotlinBuildReporterHandler().buildFinished(gradle, perfReportFile, kotlinTaskTimeNs, allTasksTimeNs) + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrLinkWithWorkers.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrLinkWithWorkers.kt index 3eebb6e5a55..0470bacad4c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrLinkWithWorkers.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrLinkWithWorkers.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.gradle.targets.js.ir import org.gradle.api.tasks.CacheableTask import org.gradle.workers.WorkerExecutor import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers +import org.jetbrains.kotlin.gradle.tasks.GradleCompileTaskProvider import javax.inject.Inject @CacheableTask @@ -16,5 +17,5 @@ internal open class KotlinJsIrLinkWithWorkers constructor( private val workerExecutor: WorkerExecutor ) : KotlinJsIrLink() { - override fun compilerRunner() = GradleCompilerRunnerWithWorkers(this, workerExecutor) + override fun compilerRunner() = GradleCompilerRunnerWithWorkers(GradleCompileTaskProvider(this), workerExecutor) } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinWithJavaTarget.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinWithJavaTarget.kt index 0cd6128a477..82e3a9956d5 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinWithJavaTarget.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinWithJavaTarget.kt @@ -10,6 +10,7 @@ import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.Project import org.gradle.api.artifacts.Dependency import org.gradle.api.plugins.JavaPlugin +import org.gradle.api.provider.Provider import org.gradle.jvm.tasks.Jar import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType @@ -44,10 +45,12 @@ open class KotlinWithJavaTarget( KotlinWithJavaCompilationFactory(project, this, kotlinOptionsFactory) ) - internal val defaultArtifactClassesListFile: File - get() { + private val layout = project.layout + + internal val defaultArtifactClassesListFile: Provider = + layout.buildDirectory.dir(KOTLIN_BUILD_DIR_NAME).map { val jarTask = project.tasks.getByName(artifactsTaskName) as Jar - return File(File(project.buildDir, KOTLIN_BUILD_DIR_NAME), "${sanitizeFileName(jarTask.archiveFileName.get())}-classes.txt") + it.file("${sanitizeFileName(jarTask.archiveFileName.get())}-classes.txt").asFile } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt index 8b60a1368f0..693c133a6a3 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt @@ -457,6 +457,31 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) : } } ) + /* + val transformedFilesByOriginalFiles = project.provider { + transformationTaskHolders + .flatMap { it.get().filesByOriginalFiles.toList() } + .groupBy({ it.first }, valueTransform = { it.second }) + } + + val builtBySet = mutableSetOf() + val resultFiles = project.files(Callable { + val originalFiles = fromFiles.toSet() + val filesToAdd = mutableSetOf() + val filesToExclude = mutableSetOf() + + transformedFilesByOriginalFiles.get().forEach { (original, replacement) -> + if (original.all { it in originalFiles }) { + builtBySet.addAll(replacement) + filesToAdd += replacement.flatMap { it.files } + filesToExclude += original + } + } + + originalFiles - filesToExclude + filesToAdd + }) + return resultFiles.builtBy(builtBySet) + */ } private fun createCommonMainElementsConfiguration(target: KotlinMetadataTarget) { diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeCompilation.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeCompilation.kt index abe10ae5ac0..f5da104ff82 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeCompilation.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeCompilation.kt @@ -10,9 +10,7 @@ import groovy.lang.Closure import org.gradle.api.Action import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.Project -import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.FileCollection -import org.gradle.api.file.SourceDirectorySet import org.gradle.api.tasks.TaskProvider import org.gradle.util.ConfigureUtil import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetConfigurator.kt index deec23371bd..c7bcae48a59 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetConfigurator.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetConfigurator.kt @@ -156,7 +156,7 @@ open class KotlinNativeTargetConfigurator( val compileTaskProvider = registerTask( compilation.compileKotlinTaskName ) { - it.compilation = compilation + it.compilation.set(compilation) it.group = BasePlugin.BUILD_GROUP it.description = "Compiles a klibrary from the '${compilation.name}' " + "compilation for target '${compilation.platformType.name}'." diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetPreset.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetPreset.kt index 212b87960c0..c837770b03a 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetPreset.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetPreset.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.gradle.targets.native.internal.PlatformLibrariesGene import org.jetbrains.kotlin.gradle.targets.native.internal.setUpKotlinNativePlatformDependencies import org.jetbrains.kotlin.gradle.utils.NativeCompilerDownloader import org.jetbrains.kotlin.gradle.utils.SingleActionPerProject +import org.jetbrains.kotlin.gradle.utils.isConfigurationCacheAvailable import org.jetbrains.kotlin.konan.CompilerVersion import org.jetbrains.kotlin.konan.target.HostManager import org.jetbrains.kotlin.konan.target.KonanTarget @@ -89,11 +90,9 @@ abstract class AbstractKotlinNativeTargetPreset( createTargetConfigurator().configureTarget(result) SingleActionPerProject.run(project, "setUpKotlinNativePlatformDependencies") { - project.gradle.addListener(object : BuildAdapter() { - override fun projectsEvaluated(gradle: Gradle) { - project.setUpKotlinNativePlatformDependencies() - } - }) + project.gradle.projectsEvaluated { + project.setUpKotlinNativePlatformDependencies() + } } if (!konanTarget.enabledOnCurrentHost) { diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/KotlinCocoapodsPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/KotlinCocoapodsPlugin.kt index 6c9e65a85b1..ce693dd7417 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/KotlinCocoapodsPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/KotlinCocoapodsPlugin.kt @@ -8,11 +8,9 @@ package org.jetbrains.kotlin.gradle.plugin.cocoapods import org.gradle.api.Plugin import org.gradle.api.Project -import org.gradle.api.Task import org.gradle.api.provider.Provider import org.gradle.api.tasks.Sync import org.gradle.api.tasks.TaskProvider -import org.jetbrains.kotlin.gradle.tasks.registerTask import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation @@ -20,10 +18,7 @@ import org.jetbrains.kotlin.gradle.plugin.addExtension import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget import org.jetbrains.kotlin.gradle.plugin.whenEvaluated import org.jetbrains.kotlin.gradle.targets.native.tasks.* -import org.jetbrains.kotlin.gradle.tasks.DefFileTask -import org.jetbrains.kotlin.gradle.tasks.DummyFrameworkTask -import org.jetbrains.kotlin.gradle.tasks.FatFrameworkTask -import org.jetbrains.kotlin.gradle.tasks.PodspecTask +import org.jetbrains.kotlin.gradle.tasks.* import org.jetbrains.kotlin.gradle.utils.asValidTaskName import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName import org.jetbrains.kotlin.konan.target.HostManager diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/performance/KotlinPerformancePlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/performance/KotlinPerformancePlugin.kt index a3be560667b..603e8f9df08 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/performance/KotlinPerformancePlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/performance/KotlinPerformancePlugin.kt @@ -16,8 +16,8 @@ import org.jetbrains.kotlin.gradle.plugin.addExtension import org.jetbrains.kotlin.gradle.plugin.mpp.disambiguateName import org.jetbrains.kotlin.gradle.targets.native.tasks.NativePerformanceReport import org.jetbrains.kotlin.gradle.tasks.registerTask +import org.jetbrains.kotlin.gradle.utils.isConfigurationCacheAvailable import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName -import java.io.File import java.util.concurrent.ConcurrentHashMap class TaskTime(val startTime: Long) { @@ -66,17 +66,26 @@ open class KotlinPerformancePlugin : Plugin { private fun configureTasks(project: Project, performanceExtension: PerformanceExtension) { // Add time listener. - val timeListener = TaskTimerListener(project) - project.gradle.addListener(timeListener) - performanceExtension.trackedBinaries.forEach { binary -> - val perfReport = project.registerTask(binary.target.disambiguateName(lowerCamelCaseName("perfReport", binary.name))) { - it.binary = binary - it.settings = performanceExtension - it.timeListener = timeListener - it.group = TASK_GROUP - it.description = "Report performance measurement results for binary '${binary.name}' of target '${binary.target.name}'." + if (!isConfigurationCacheAvailable(project.gradle)) { + val timeListener = TaskTimerListener(project) + project.gradle.addListener(timeListener) + performanceExtension.trackedBinaries.forEach { binary -> + val perfReport = project.registerTask( + binary.target.disambiguateName( + lowerCamelCaseName( + "perfReport", + binary.name + ) + ) + ) { + it.binary = binary + it.settings = performanceExtension + it.timeListener = timeListener + it.group = TASK_GROUP + it.description = "Report performance measurement results for binary '${binary.name}' of target '${binary.target.name}'." + } + binary.linkTaskProvider.configure { linkTask -> linkTask.finalizedBy(perfReport) } } - binary.linkTaskProvider.configure { linkTask -> linkTask.finalizedBy(perfReport) } } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativePerformanceTask.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativePerformanceTask.kt index 866e9941151..b9be837494d 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativePerformanceTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativePerformanceTask.kt @@ -70,15 +70,15 @@ open class NativePerformanceReport : DefaultTask() { } private fun getPerformanceCompilerOptions() = - (compilerFlagsFromBinary() + binary.linkTask.compilation.kotlinOptions.freeCompilerArgs) + (compilerFlagsFromBinary() + binary.linkTask.compilation.get().kotlinOptions.freeCompilerArgs) .filter { it in listOf("-g", "-opt", "-Xg0") }.map { "\"$it\"" } @TaskAction fun generate() { val compileTasks = if (settings.includeAssociatedTasks) - getAllExecutedTasks(binary.linkTask.compilation) + getAllExecutedTasks(binary.linkTask.compilation.get()) else - listOf(binary.linkTask.compilation.compileKotlinTask) + listOf(binary.linkTask.compilation.get().compileKotlinTask) val allExecutedTasks = listOf(binary.linkTask) + compileTasks val upToDateTasks = allExecutedTasks.filter { it.state.upToDate }.map { it.name } if (upToDateTasks.isNotEmpty()) { diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativeTasks.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativeTasks.kt index 68876b15901..17285dcea37 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativeTasks.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativeTasks.kt @@ -13,8 +13,8 @@ import org.gradle.api.artifacts.* import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.FileCollection import org.gradle.api.file.FileTree -import org.gradle.api.file.SourceDirectorySet import org.gradle.api.logging.Logger +import org.gradle.api.provider.Property import org.gradle.api.provider.Provider import org.gradle.api.tasks.* import org.gradle.api.tasks.compile.AbstractCompile @@ -26,12 +26,12 @@ import org.jetbrains.kotlin.gradle.dsl.NativeCacheKind import org.jetbrains.kotlin.gradle.internal.ensureParentDirsCreated import org.jetbrains.kotlin.gradle.plugin.LanguageSettingsBuilder import org.jetbrains.kotlin.gradle.plugin.cocoapods.asValidFrameworkName -import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion import org.jetbrains.kotlin.gradle.plugin.mpp.* import org.jetbrains.kotlin.gradle.plugin.sources.DefaultLanguageSettingsBuilder import org.jetbrains.kotlin.gradle.targets.native.internal.isAllowCommonizer import org.jetbrains.kotlin.gradle.utils.getValue import org.jetbrains.kotlin.gradle.utils.klibModuleName +import org.jetbrains.kotlin.gradle.utils.newProperty import org.jetbrains.kotlin.gradle.utils.listFilesOrEmpty import org.jetbrains.kotlin.konan.library.KLIB_INTEROP_IR_PROVIDER_IDENTIFIER import org.jetbrains.kotlin.konan.properties.saveToFile @@ -112,7 +112,7 @@ private fun Collection.filterKlibsPassedToCompiler(project: Project) = fil } // endregion -abstract class AbstractKotlinNativeCompile : AbstractCompile() { +abstract class AbstractKotlinNativeCompile : AbstractCompile() { init { sourceCompatibility = "1.6" @@ -120,7 +120,7 @@ abstract class AbstractKotlinNativeCompile : Abstra } @get:Internal - abstract val compilation: AbstractKotlinNativeCompilation + abstract val compilation: Provider // region inputs/outputs @get:Input @@ -136,20 +136,21 @@ abstract class AbstractKotlinNativeCompile : Abstra abstract val baseName: String // Inputs and outputs - val libraries: FileCollection - @InputFiles get() = - // Avoid resolving these dependencies during task graph construction when we can't build the target: - if (compilation.konanTarget.enabledOnCurrentHost) - compilation.compileDependencyFiles.filterOutPublishableInteropLibs(project) - else project.files() + @get:InputFiles + val libraries: FileCollection by project.provider { + // Avoid resolving these dependencies during task graph construction when we can't build the target: + if (compilation.get().konanTarget.enabledOnCurrentHost) + compilation.get().compileDependencyFiles.filterOutPublishableInteropLibs(project) + else project.files() + } override fun getClasspath(): FileCollection = libraries override fun setClasspath(configuration: FileCollection?) { throw UnsupportedOperationException("Setting classpath directly is unsupported.") } - val target: String - @Input get() = compilation.konanTarget.name + @get:Input + val target: String by project.provider { compilation.get().konanTarget.name } // region Compiler options. @get:Internal @@ -158,11 +159,12 @@ abstract class AbstractKotlinNativeCompile : Abstra abstract fun kotlinOptions(fn: Closure<*>) @get:Input - abstract val additionalCompilerOptions: Collection + abstract val additionalCompilerOptions: Provider> @get:Internal - val languageSettings: LanguageSettingsBuilder - get() = compilation.defaultSourceSet.languageSettings + val languageSettings: LanguageSettingsBuilder by lazy { + compilation.get().defaultSourceSet.languageSettings + } @get:Input val progressiveMode: Boolean @@ -170,8 +172,8 @@ abstract class AbstractKotlinNativeCompile : Abstra // endregion. @get:Input - val enableEndorsedLibs: Boolean - get() = compilation.enableEndorsedLibs + val enableEndorsedLibs by + project.provider { compilation.map { it.enableEndorsedLibs }.get() } val kotlinNativeVersion: String @Input get() = project.konanVersion.toString() @@ -179,11 +181,11 @@ abstract class AbstractKotlinNativeCompile : Abstra // OutputFile is located under the destinationDir, so there is no need to register it as a separate output. @Internal val outputFile: Provider = project.provider { - val konanTarget = compilation.konanTarget + val konanTarget = compilation.get().konanTarget val prefix = outputKind.prefix(konanTarget) val suffix = outputKind.suffix(konanTarget) - val filename = "$prefix$baseName$suffix".let { + val filename = "$prefix${baseName}$suffix".let { when { outputKind == FRAMEWORK -> it.asValidFrameworkName() @@ -241,10 +243,10 @@ abstract class AbstractKotlinNativeCompile : Abstra addKey("-progressive", progressiveMode) if (!defaultsOnly) { - addAll(additionalCompilerOptions) + addAll(additionalCompilerOptions.get()) } - (compilation.defaultSourceSet.languageSettings as? DefaultLanguageSettingsBuilder)?.run { + (compilation.get().defaultSourceSet.languageSettings as? DefaultLanguageSettingsBuilder)?.run { addAll(freeCompilerArgs) } } @@ -274,7 +276,7 @@ abstract class AbstractKotlinNativeCompile : Abstra addArg("-target", target) addArg("-p", outputKind.name.toLowerCase()) - if (compilation is KotlinSharedNativeCompilation) { + if (compilation.get() is KotlinSharedNativeCompilation) { add("-Xexpect-actual-linker") add("-Xmetadata-klib") addArg("-manifest", manifestFile.get().absolutePath) @@ -301,7 +303,7 @@ abstract class AbstractKotlinNativeCompile : Abstra val output = outputFile.get() output.parentFile.mkdirs() - if (compilation is KotlinSharedNativeCompilation) { + if (compilation.get() is KotlinSharedNativeCompilation) { val manifestFile: File = manifestFile.get() manifestFile.ensureParentDirsCreated() val properties = java.util.Properties() @@ -316,9 +318,12 @@ abstract class AbstractKotlinNativeCompile : Abstra /** * A task producing a klibrary from a compilation. */ -open class KotlinNativeCompile : AbstractKotlinNativeCompile(), KotlinCompile { +open class KotlinNativeCompile : AbstractKotlinNativeCompile(), + KotlinCompile { @Internal - final override lateinit var compilation: AbstractKotlinNativeCompilation + @Transient // can't be serialized for Gradle configuration avoidance + final override val compilation: Property = + project.newProperty() @get:Input override val outputKind = LIBRARY @@ -330,8 +335,11 @@ open class KotlinNativeCompile : AbstractKotlinNativeCompile + project.files( + compilationInstance.associateWithTransitiveClosure.map { it.output.allOutputs }, + compilationInstance.friendArtifacts ) + } // endregion. // region Language settings imported from a SourceSet. @@ -372,11 +388,12 @@ open class KotlinNativeCompile : AbstractKotlinNativeCompile - get() = kotlinOptions.freeCompilerArgs + override val additionalCompilerOptions: Provider> = project.provider { + kotlinOptions.freeCompilerArgs + } override fun kotlinOptions(fn: KotlinCommonOptions.() -> Unit) { kotlinOptions.fn() @@ -409,7 +426,7 @@ open class KotlinNativeCompile : AbstractKotlinNativeCompile() { +open class KotlinNativeLink : AbstractKotlinNativeCompile() { + + @get:Internal + @Transient // can't be serialized for Gradle configuration avoidance + final override val compilation: Property = project.newProperty() { binary.compilation } init { - dependsOn(project.provider { compilation.compileKotlinTask }) + dependsOn(project.provider { compilation.get().compileKotlinTask }) } @Internal + @Transient lateinit var binary: NativeBinary - @get:Internal - override val compilation: KotlinNativeCompilation - get() = binary.compilation - @Internal // Taken into account by getSources(). - val intermediateLibrary: Provider = project.provider { - compilation.compileKotlinTask.outputFile.get() + val intermediateLibrary: Provider = compilation.map { compilationInstance -> + compilationInstance.compileKotlinTask.outputFile.get() } @InputFiles @@ -459,27 +477,23 @@ open class KotlinNativeLink : AbstractKotlinNativeCompile - get() = kotlinOptions.freeCompilerArgs + compilation.kotlinOptions.freeCompilerArgs + override val additionalCompilerOptions: Provider> = compilation.map { compilationInstance -> + kotlinOptions.freeCompilerArgs + compilationInstance.kotlinOptions.freeCompilerArgs + } override val kotlinOptions: KotlinCommonToolOptions = NativeLinkOptions() @@ -503,18 +518,16 @@ open class KotlinNativeLink : AbstractKotlinNativeCompile - get() = binary.linkerOpts + @Input get() = binary.linkerOpts - @get:Input val processTests: Boolean - get() = binary is TestExecutable + @Input get() = binary is TestExecutable @get:InputFiles val exportLibraries: FileCollection @@ -527,12 +540,14 @@ open class KotlinNativeLink : AbstractKotlinNativeCompile = mutableListOf().apply { addAll(super.buildCompilerArgs()) @@ -569,9 +584,12 @@ open class KotlinNativeLink : AbstractKotlinNativeCompile = listOf("-Xinclude=${intermediateLibrary.get().absolutePath}") + @get:Internal + val apiFilesProvider = compilation.map {project.configurations.getByName(it.apiConfigurationName).files.filterKlibsPassedToCompiler(project)} + private fun validatedExportedLibraries() { val exportConfiguration = exportLibraries as? Configuration ?: return - val apiFiles = project.configurations.getByName(compilation.apiConfigurationName).files.filterKlibsPassedToCompiler(project) + val apiFiles = apiFilesProvider.get() val failed = mutableSetOf() exportConfiguration.allDependencies.forEach { @@ -765,7 +783,11 @@ internal class CacheBuilder(val project: Project, val binary: NativeBinary) { private val String.cachedName get() = getCacheFileName(this, konanCacheKind) - private fun ensureCompilerProvidedLibPrecached(platformLibName: String, platformLibs: Map, visitedLibs: MutableSet) { + private fun ensureCompilerProvidedLibPrecached( + platformLibName: String, + platformLibs: Map, + visitedLibs: MutableSet + ) { if (platformLibName in visitedLibs) return visitedLibs += platformLibName diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/InspectClassesForMultiModuleIC.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/InspectClassesForMultiModuleIC.kt index 0c3a812367a..ce8025d80ac 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/InspectClassesForMultiModuleIC.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/InspectClassesForMultiModuleIC.kt @@ -11,6 +11,7 @@ import org.gradle.api.plugins.JavaPluginConvention import org.gradle.api.tasks.* import org.jetbrains.kotlin.gradle.dsl.KotlinSingleJavaTargetExtension import org.jetbrains.kotlin.gradle.dsl.kotlinExtension +import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast import org.jetbrains.kotlin.gradle.utils.newProperty import java.io.File @@ -27,18 +28,35 @@ internal open class InspectClassesForMultiModuleIC : DefaultTask() { @Suppress("MemberVisibilityCanBePrivate") @get:OutputFile internal val classesListFile: File by lazy { - (project.kotlinExtension as KotlinSingleJavaTargetExtension).target.defaultArtifactClassesListFile + (project.kotlinExtension as KotlinSingleJavaTargetExtension).target.defaultArtifactClassesListFile.get() } + @get:InputFiles + internal val sourceSetOutputClassesDir by lazy { + project.convention.findPlugin(JavaPluginConvention::class.java)?.sourceSets?.findByName(sourceSetName)?.output?.classesDirs + } + + @get:Internal + internal val fileTrees + get() = sourceSetOutputClassesDir?.map { + if (isGradleVersionAtLeast(6, 0)) { + objects.fileTree().from(it).include("**/*.class") + } else { + project.fileTree(it).include("**/*.class") + } + } + + @get:Internal + internal val objects = project.objects + @Suppress("MemberVisibilityCanBePrivate") @get:InputFiles internal val classFiles: FileCollection get() { - val convention = project.convention.findPlugin(JavaPluginConvention::class.java) - val sourceSet = convention?.sourceSets?.findByName(sourceSetName) ?: return project.files() - - val fileTrees = sourceSet.output.classesDirs.map { project.fileTree(it).include("**/*.class") } - return project.files(fileTrees) + if (sourceSetOutputClassesDir != null) { + return objects.fileCollection().from(fileTrees) + } + return objects.fileCollection() } @TaskAction diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompileCommon.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompileCommon.kt index 79fb4d61739..fb4e0155293 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompileCommon.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompileCommon.kt @@ -96,7 +96,7 @@ open class KotlinCompileCommon : AbstractKotlinCompile @get:Classpath @get:InputFiles - internal val computedCompilerClasspath: List by project.provider { + internal val computedCompilerClasspath: List by lazy { compilerClasspath?.takeIf { it.isNotEmpty() } ?: compilerJarFile?.let { // a hack to remove compiler jar from the cp, will be dropped when compilerJarFile will be removed @@ -113,19 +119,51 @@ abstract class AbstractKotlinCompileTool protected abstract fun findKotlinCompilerClasspath(project: Project): List } +public class GradleCompileTaskProvider { + + constructor(task: Task) { + buildDir = task.project.buildDir + projectDir = task.project.rootProject.projectDir + rootDir = task.project.rootProject.rootDir + sessionsDir = GradleCompilerRunner.sessionsDir(task.project) + projectName = task.project.rootProject.name.normalizeForFlagFile() + buildModulesInfo = GradleCompilerRunner.buildModulesInfo(task.project.gradle) + path = task.path + logger = task.logger + + } + + val path: String + val logger: Logger + val buildDir: File /*= project.buildDir*/ + val projectDir: File /*= project.rootProject.projectDir*/ + val rootDir: File /*= project.rootProject.rootDir*/ + val sessionsDir: File/* = GradleCompilerRunner.sessionsDir(project)*/ + val projectName: String /*= project.rootProject.name.normalizeForFlagFile()*/ + val buildModulesInfo: IncrementalModuleInfo /*= GradleCompilerRunner.buildModulesInfo(project.gradle)*/ +} + abstract class AbstractKotlinCompile() : AbstractKotlinCompileTool() { init { cacheOnlyIfEnabledForKotlin() } + @get:Internal + private val layout = project.layout + // avoid creating directory in getter: this can lead to failure in parallel build @get:LocalState - internal val taskBuildDirectory: File by project.provider { - File(File(project.buildDir, KOTLIN_BUILD_DIR_NAME), name) - } + internal val taskBuildDirectory: File by layout.buildDirectory.dir(KOTLIN_BUILD_DIR_NAME).map { it.file(name).asFile } - override fun localStateDirectories(): FileCollection = project.files(taskBuildDirectory) + + @get:Internal + internal val projectObjects = project.objects + + @get:LocalState + internal val localStateDirectoriesProvider: FileCollection = projectObjects.fileCollection().from(taskBuildDirectory) + + override fun localStateDirectories(): FileCollection = localStateDirectoriesProvider // indicates that task should compile kotlin incrementally if possible // it's not possible when IncrementalTaskInputs#isIncremental returns false (i.e first build) @@ -147,9 +185,7 @@ abstract class AbstractKotlinCompile() : AbstractKo internal var buildReportMode: BuildReportMode? = null @get:Internal - internal val taskData: KotlinCompileTaskData by project.provider { - KotlinCompileTaskData.get(project, name) - } + internal val taskData: KotlinCompileTaskData = KotlinCompileTaskData.get(project, name) @get:Input internal open var useModuleDetection: Boolean @@ -162,11 +198,9 @@ abstract class AbstractKotlinCompile() : AbstractKo protected val multiModuleICSettings: MultiModuleICSettings get() = MultiModuleICSettings(taskData.buildHistoryFile, useModuleDetection) - @get:Classpath @get:InputFiles - val pluginClasspath: FileCollection by project.provider { - project.configurations.getByName(PLUGIN_CLASSPATH_CONFIGURATION_NAME) - } + @get:Classpath + val pluginClasspath: FileCollection = project.configurations.getByName(PLUGIN_CLASSPATH_CONFIGURATION_NAME) @get:Internal internal val pluginOptions = CompilerPluginOptions() @@ -175,10 +209,13 @@ abstract class AbstractKotlinCompile() : AbstractKo @get:InputFiles protected val additionalClasspath = arrayListOf() + @get:Internal + internal val objects = project.objects + // Store this file collection before it is filtered by File::exists to ensure that Gradle Instant execution doesn't serialize the // filtered files, losing those that don't exist yet and will only be created during build private val compileClasspathImpl by project.provider { - classpath + project.files(additionalClasspath) + classpath + objects.fileCollection().from(additionalClasspath) } @get:Internal // classpath already participates in the checks @@ -197,8 +234,9 @@ abstract class AbstractKotlinCompile() : AbstractKo sourceFilesExtensionsSources.add(extensions) } - private val kotlinExt: KotlinProjectExtension - get() = project.extensions.findByType(KotlinProjectExtension::class.java)!! + @get:Internal + @field:Transient + internal val kotlinExtProvider: KotlinProjectExtension = project.extensions.findByType(KotlinProjectExtension::class.java)!! override fun getDestinationDir(): File = taskData.destinationDir.get() @@ -218,13 +256,13 @@ abstract class AbstractKotlinCompile() : AbstractKo @get:Internal internal var coroutinesFromGradleProperties: Coroutines? = null // Input is needed to force rebuild even if source files are not changed - @get:Input - internal val coroutinesStr: String - get() = coroutines.name - @get:Internal - internal val coroutines: Coroutines by project.provider { - kotlinExt.experimental.coroutines + @get:Input + internal val coroutinesStr: Provider = project.provider {coroutines.get().name} + + @get:Input + internal val coroutines: Provider = project.provider { + kotlinExtProvider.experimental.coroutines ?: coroutinesFromGradleProperties ?: Coroutines.DEFAULT } @@ -242,7 +280,7 @@ abstract class AbstractKotlinCompile() : AbstractKo @get:InputFiles @get:PathSensitive(PathSensitivity.RELATIVE) - internal var commonSourceSet: FileCollection = project.files() + internal var commonSourceSet: FileCollection = project.files() //TODO @get:Input internal val moduleName: String by project.provider { @@ -262,8 +300,10 @@ abstract class AbstractKotlinCompile() : AbstractKo private val kotlinLogger by lazy { GradleKotlinLogger(logger) } - internal open fun compilerRunner(): GradleCompilerRunner = - GradleCompilerRunner(this) + @get:Internal + internal val compilerRunner by lazy { compilerRunner() } + + internal open fun compilerRunner(): GradleCompilerRunner = GradleCompilerRunner(GradleCompileTaskProvider(this)) @TaskAction fun execute(inputs: IncrementalTaskInputs) { @@ -297,6 +337,9 @@ abstract class AbstractKotlinCompile() : AbstractKo return !inputs.isIncremental && getSourceRoots().kotlinSourceFiles.isEmpty() } + @get:Internal + private val projectDir = project.rootProject.projectDir + private fun executeImpl(inputs: IncrementalTaskInputs) { // Check that the JDK tools are available in Gradle (fail-fast, instead of a fail during the compiler run): findToolsJar() @@ -304,7 +347,7 @@ abstract class AbstractKotlinCompile() : AbstractKo val sourceRoots = getSourceRoots() val allKotlinSources = sourceRoots.kotlinSourceFiles - logger.kotlinDebug { "All kotlin sources: ${allKotlinSources.pathsAsStringRelativeTo(project.rootProject.projectDir)}" } + logger.kotlinDebug { "All kotlin sources: ${allKotlinSources.pathsAsStringRelativeTo(projectDir)}" } if (skipCondition(inputs)) { // Skip running only if non-incremental run. Otherwise, we may need to do some cleanup. @@ -327,13 +370,14 @@ abstract class AbstractKotlinCompile() : AbstractKo */ internal abstract fun callCompilerAsync(args: T, sourceRoots: SourceRoots, changedFiles: ChangedFiles) + @get:Input + internal val isMultiplatform: Boolean by lazy { project.plugins.any { it is KotlinPlatformPluginBase || it is KotlinMultiplatformPluginWrapper } } + @get:Internal - internal val isMultiplatform: Boolean by project.provider { - project.plugins.any { it is KotlinPlatformPluginBase || it is KotlinMultiplatformPluginWrapper } - } + internal val abstractKotlinCompileArgumentsContributor by lazy { AbstractKotlinCompileArgumentsContributor(KotlinCompileArgumentsProvider(this)) } override fun setupCompilerArgs(args: T, defaultsOnly: Boolean, ignoreClasspathResolutionErrors: Boolean) { - AbstractKotlinCompileArgumentsContributor(thisTaskProvider).contributeArguments( + abstractKotlinCompileArgumentsContributor.contributeArguments( args, compilerArgumentsConfigurationFlags(defaultsOnly, ignoreClasspathResolutionErrors) ) @@ -350,6 +394,45 @@ abstract class AbstractKotlinCompile() : AbstractKo } } +open class KotlinCompileArgumentsProvider>(taskProvider: T) { + + val coroutines: Provider + val logger: Logger + val isMultiplatform: Boolean + val pluginClasspath: FileCollection + val pluginOptions: CompilerPluginOptions + + init { + coroutines = taskProvider.coroutines + logger = taskProvider.logger + isMultiplatform = taskProvider.isMultiplatform + pluginClasspath = taskProvider.pluginClasspath + pluginOptions = taskProvider.pluginOptions + } +} + +class KotlinJvmCompilerArgumentsProvider + (taskProvider: KotlinCompile) : KotlinCompileArgumentsProvider(taskProvider) { + + val moduleName: String + val friendPaths: Array + val compileClasspath: Iterable + val destinationDir: File + internal val kotlinOptions: List + + init { +// super(taskProvider) + moduleName = taskProvider.moduleName + friendPaths = taskProvider.friendPaths + compileClasspath = taskProvider.compileClasspath + destinationDir = taskProvider.destinationDir + kotlinOptions = listOfNotNull( + taskProvider.parentKotlinOptionsImpl as KotlinJvmOptionsImpl?, + taskProvider.kotlinOptions as KotlinJvmOptionsImpl + ) + } +} + internal inline val T.thisTaskProvider: TaskProvider get() = checkNotNull(project.locateTask(name)) @@ -397,8 +480,8 @@ open class KotlinCompile : AbstractKotlinCompile(), Kotl } @get:Internal - internal val compilerArgumentsContributor: CompilerArgumentsContributor by project.provider { - KotlinJvmCompilerArgumentsContributor(thisTaskProvider) + internal val compilerArgumentsContributor: CompilerArgumentsContributor by lazy { + KotlinJvmCompilerArgumentsContributor(KotlinJvmCompilerArgumentsProvider(this)) } @Internal @@ -409,7 +492,7 @@ open class KotlinCompile : AbstractKotlinCompile(), Kotl val messageCollector = GradlePrintingMessageCollector(logger, args.allWarningsAsErrors) val outputItemCollector = OutputItemsCollectorImpl() - val compilerRunner = compilerRunner() + val compilerRunner = compilerRunner val icEnv = if (isIncrementalCompilationEnabled()) { logger.info(USING_JVM_INCREMENTAL_COMPILATION_MESSAGE) @@ -417,7 +500,7 @@ open class KotlinCompile : AbstractKotlinCompile(), Kotl if (hasFilesInTaskBuildDirectory()) changedFiles else ChangedFiles.Unknown(), taskBuildDirectory, usePreciseJavaTracking = usePreciseJavaTracking, - disableMultiModuleIC = disableMultiModuleIC(), + disableMultiModuleIC = disableMultiModuleIC, multiModuleICSettings = multiModuleICSettings ) } else null @@ -439,31 +522,32 @@ open class KotlinCompile : AbstractKotlinCompile(), Kotl ) } - private fun disableMultiModuleIC(): Boolean { - if (!isIncrementalCompilationEnabled() || javaOutputDir == null) return false + @get:Input + val disableMultiModuleIC: Boolean by lazy { + if (!isIncrementalCompilationEnabled() || javaOutputDir == null) { + false + } else { - var illegalTaskOrNull: AbstractCompile? = null - - project.tasks.configureEach { - if (it is AbstractCompile && - it !is JavaCompile && - it !is AbstractKotlinCompile<*> && - javaOutputDir!!.isParentOf(it.destinationDir) - ) { - illegalTaskOrNull = illegalTaskOrNull ?: it + var illegalTaskOrNull: AbstractCompile? = null + project.tasks.configureEach { + if (it is AbstractCompile && + it !is JavaCompile && + it !is AbstractKotlinCompile<*> && + javaOutputDir!!.isParentOf(it.destinationDir) + ) { + illegalTaskOrNull = illegalTaskOrNull ?: it + } } + if (illegalTaskOrNull != null) { + val illegalTask = illegalTaskOrNull!! + logger.info( + "Kotlin inter-project IC is disabled: " + + "unknown task '$illegalTask' destination dir ${illegalTask.destinationDir} " + + "intersects with java destination dir $javaOutputDir" + ) + } + illegalTaskOrNull != null } - - illegalTaskOrNull?.let { illegalTask -> - logger.info( - "Kotlin inter-project IC is disabled: " + - "unknown task '$illegalTask' destination dir ${illegalTask.destinationDir} " + - "intersects with java destination dir $javaOutputDir" - ) - return true - } - - return false } // override setSource to track source directory sets and files (for generated android folders) @@ -483,21 +567,26 @@ open class KotlinCompile : AbstractKotlinCompile(), Kotl internal open class KotlinCompileWithWorkers @Inject constructor( private val workerExecutor: WorkerExecutor ) : KotlinCompile() { - override fun compilerRunner() = GradleCompilerRunnerWithWorkers(this, workerExecutor) + + @get:Internal + val compilerRunnerValue = GradleCompilerRunnerWithWorkers(GradleCompileTaskProvider(this), workerExecutor) + + override fun compilerRunner() = compilerRunnerValue } @CacheableTask internal open class Kotlin2JsCompileWithWorkers @Inject constructor( private val workerExecutor: WorkerExecutor ) : Kotlin2JsCompile() { - override fun compilerRunner() = GradleCompilerRunnerWithWorkers(this, workerExecutor) + + override fun compilerRunner() = GradleCompilerRunnerWithWorkers(GradleCompileTaskProvider(this), workerExecutor) } @CacheableTask internal open class KotlinCompileCommonWithWorkers @Inject constructor( private val workerExecutor: WorkerExecutor ) : KotlinCompileCommon() { - override fun compilerRunner() = GradleCompilerRunnerWithWorkers(this, workerExecutor) + override fun compilerRunner() = GradleCompilerRunnerWithWorkers(GradleCompileTaskProvider(this), workerExecutor) } @CacheableTask @@ -507,8 +596,7 @@ open class Kotlin2JsCompile : AbstractKotlinCompile(), Ko incremental = true } - override val kotlinOptions: KotlinJsOptions - get() = taskData.compilation.kotlinOptions as KotlinJsOptions + override val kotlinOptions = taskData.compilation.kotlinOptions as KotlinJsOptions @get:Internal protected val defaultOutputFile: File @@ -601,6 +689,9 @@ open class Kotlin2JsCompile : AbstractKotlinCompile(), Ko JsLibraryUtils::isKotlinJavascriptLibrary } + @get:Internal + internal val absolutePathProvider = project.projectDir.absolutePath + override fun callCompilerAsync(args: K2JSCompilerArguments, sourceRoots: SourceRoots, changedFiles: ChangedFiles) { sourceRoots as SourceRoots.KotlinOnly @@ -624,14 +715,14 @@ open class Kotlin2JsCompile : AbstractKotlinCompile(), Ko args.friendModules = friendDependencies.joinToString(File.pathSeparator) if (args.sourceMapBaseDirs == null && !args.sourceMapPrefix.isNullOrEmpty()) { - args.sourceMapBaseDirs = project.projectDir.absolutePath + args.sourceMapBaseDirs = absolutePathProvider } logger.kotlinDebug("compiling with args ${ArgumentUtils.convertArgumentsToStringList(args)}") val messageCollector = GradlePrintingMessageCollector(logger, args.allWarningsAsErrors) val outputItemCollector = OutputItemsCollectorImpl() - val compilerRunner = compilerRunner() + val compilerRunner = compilerRunner val icEnv = if (isIncrementalCompilationEnabled()) { logger.info(USING_JS_INCREMENTAL_COMPILATION_MESSAGE) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/ParsedGradleVersion.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/ParsedGradleVersion.kt index 924014ed78f..2312e3d8a7c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/ParsedGradleVersion.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/ParsedGradleVersion.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.gradle.utils +import org.gradle.api.invocation.Gradle import org.gradle.util.GradleVersion internal data class ParsedGradleVersion(val major: Int, val minor: Int) : Comparable { @@ -56,4 +57,12 @@ internal data class ParsedGradleVersion(val major: Int, val minor: Int) : Compar fun isGradleVersionAtLeast(major: Int, minor: Int) = ParsedGradleVersion.parse(GradleVersion.current().version) - ?.let { it >= ParsedGradleVersion(major, minor) } ?: false \ No newline at end of file + ?.let { it >= ParsedGradleVersion(major, minor) } ?: false + +fun isConfigurationCacheAvailable(gradle: Gradle) = + try { + val startParameters = gradle.startParameter + startParameters.javaClass.getMethod("isConfigurationCache").invoke(startParameters) as? Boolean + } catch (_: Exception) { + null + } ?: false diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/providerApiUtils.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/providerApiUtils.kt index c4930890707..b53f5369cdf 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/providerApiUtils.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/providerApiUtils.kt @@ -6,7 +6,6 @@ package org.jetbrains.kotlin.gradle.utils import org.gradle.api.Project -import org.gradle.api.file.RegularFile import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.Property import org.gradle.api.provider.Provider @@ -20,14 +19,6 @@ internal operator fun Property.setValue(thisRef: Any?, property: KPropert set(value) } -internal fun Project.newProperty(initialize: (() -> T)? = null): Property = - @Suppress("UNCHECKED_CAST") - // use Any and not T::class to allow using lists and maps as the property type, which is otherwise not allowed - (project.objects.property(Any::class.java) as Property).apply { - if (initialize != null) - set(provider(initialize)) - } - private class OptionalProviderDelegate(private val provider: Provider) : ReadOnlyProperty { override fun getValue(thisRef: Any?, property: KProperty<*>): T? = if (provider.isPresent) @@ -38,6 +29,13 @@ private class OptionalProviderDelegate(private val provider: Provider) : internal fun Project.optionalProvider(initialize: () -> T?): ReadOnlyProperty = OptionalProviderDelegate(provider(initialize)) +internal fun Project.newProperty(initialize: (() -> T)? = null): Property = + @Suppress("UNCHECKED_CAST") + (project.objects.property(Any::class.java) as Property).apply { + if (initialize != null) + set(provider(initialize)) + } + // Before 5.0 fileProperty is created via ProjectLayout // https://docs.gradle.org/current/javadoc/org/gradle/api/model/ObjectFactory.html#fileProperty-- internal fun Project.newFileProperty(initialize: (() -> File)? = null): RegularFileProperty {