diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/build.gradle.kts index 8eae65cbef2..d47ad3c21d2 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/build.gradle.kts +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/build.gradle.kts @@ -15,6 +15,7 @@ val kotlinGradlePluginTest = project(":kotlin-gradle-plugin").sourceSets.named(" dependencies { testImplementation(project(":kotlin-gradle-plugin")) + testImplementation(project(":kotlin-project-model")) testImplementation(project(":kotlin-tooling-metadata")) testImplementation(kotlinGradlePluginTest) testImplementation(project(":kotlin-gradle-subplugin-example")) 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 c2838721e1f..82302986340 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 @@ -780,6 +780,15 @@ Finished executing task ':$taskName'| fun CompiledProject.javaClassesDir(subproject: String? = null, sourceSet: String = "main"): String = project.classesDir(subproject, sourceSet, language = "java") + fun CompiledProject.compilerArgs(taskName: String): String { + val pattern = "$taskName Kotlin compiler args: " + return output + .lineSequence() + .firstOrNull { it.contains(pattern) } + ?.substringAfter(pattern) + ?: throw AssertionError("Cant find compiler args for task: $taskName") + } + private fun Project.createBuildCommand(wrapperDir: File, params: Array, options: BuildOptions): List = createGradleCommand(wrapperDir, createGradleTailParameters(options, params)) diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KpmCompilerPluginIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KpmCompilerPluginIT.kt new file mode 100644 index 00000000000..df2768d0d07 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KpmCompilerPluginIT.kt @@ -0,0 +1,65 @@ +/* + * Copyright 2010-2018 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.junit.Test +import kotlin.test.assertTrue + +class KpmCompilerPluginIT : BaseGradleIT() { + + @Test + fun testSensitivePluginOptions() { + val project = transformProjectWithPluginsDsl("kpmSensitivePluginOptions") + fun updatePluginOptions(sensitiveValue: String, insensitiveValue: String) { + project.gradleProperties().writeText( + """ + test-plugin.sensitive=$sensitiveValue + test-plugin.insensitive=$insensitiveValue + """.trimIndent() + ) + } + + updatePluginOptions("XXX", "YYY") + project.build("compileKotlin") { + assertSuccessful() + assertTasksExecuted(":compileKotlinJvm") + compilerArgs(":compileKotlinJvm").also { args -> + assertTrue( + args.contains("plugin:test-plugin:sensitive=XXX"), + "Expected sensitive plugin option in compilation args" + ) + assertTrue( + args.contains("plugin:test-plugin:insensitive=YYY"), + "Expected insensitive plugin option in compilation args" + ) + } + } + + // When insensitive plugin option change + updatePluginOptions("XXX", "ZZZ") + project.build("compileKotlin") { + assertSuccessful() + assertTasksUpToDate(":compileKotlinJvm") + } + + // When sensitive plugin option change + updatePluginOptions("ZZZ", "ZZZ") + project.build("compileKotlin") { + assertSuccessful() + assertTasksExecuted(":compileKotlinJvm") + compilerArgs(":compileKotlinJvm").also { args -> + assertTrue( + args.contains("plugin:test-plugin:sensitive=ZZZ"), + "Expected sensitive plugin option in compilation args" + ) + assertTrue( + args.contains("plugin:test-plugin:insensitive=ZZZ"), + "Expected insensitive plugin option in compilation args" + ) + } + } + } +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kpmSensitivePluginOptions/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kpmSensitivePluginOptions/build.gradle.kts new file mode 100644 index 00000000000..9cc675e2825 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kpmSensitivePluginOptions/build.gradle.kts @@ -0,0 +1,55 @@ +import org.gradle.api.Project +import org.gradle.api.plugins.ExtraPropertiesExtension +import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.* +import org.jetbrains.kotlin.project.model.* + +plugins { + kotlin("multiplatform.pm20") +} + +repositories { + mavenCentral() + mavenLocal() +} + +plugins.apply(GradleKpmPluginWithSensitivePluginOptions::class.java) + +configure { + main { + jvm + } +} + +class KpmPluginWithSensitivePluginOptions( + private val sensitiveOptionValue: String, + private val insensitiveOptionValue: String +) : KpmCompilerPlugin { + private fun pluginData() = PluginData( + pluginId = "test-plugin", + // allopen artifact is used to avoid boilerplate with cooking custom compiler plugin + artifact = PluginData.ArtifactCoordinates("org.jetbrains.kotlin", "kotlin-allopen"), + options = listOf( + StringOption("sensitive", sensitiveOptionValue, true), + StringOption("insensitive", insensitiveOptionValue, false) + ) + ) + + override fun forMetadataCompilation(fragment: KotlinModuleFragment) = pluginData() + override fun forNativeMetadataCompilation(fragment: KotlinModuleFragment) = pluginData() + override fun forPlatformCompilation(variant: KotlinModuleVariant) = pluginData() +} + +class GradleKpmPluginWithSensitivePluginOptions : GradleKpmCompilerPlugin { + private lateinit var project: Project + + override fun apply(target: Project) { + project = target + } + + override val kpmCompilerPlugin by lazy { + KpmPluginWithSensitivePluginOptions( + sensitiveOptionValue = project.property("test-plugin.sensitive") as String, + insensitiveOptionValue = project.property("test-plugin.insensitive") as String + ) + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kpmSensitivePluginOptions/gradle.properties b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kpmSensitivePluginOptions/gradle.properties new file mode 100644 index 00000000000..e58db95cd08 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kpmSensitivePluginOptions/gradle.properties @@ -0,0 +1,2 @@ +test-plugin.sensitive=SENSITIVE_VALUE +test-plugin.insensitive=INSENSITIVE_VALUE \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kpmSensitivePluginOptions/settings.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kpmSensitivePluginOptions/settings.gradle.kts new file mode 100644 index 00000000000..d560b7f93ac --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kpmSensitivePluginOptions/settings.gradle.kts @@ -0,0 +1,12 @@ +pluginManagement { + repositories { + mavenLocal() + mavenCentral() + } + + plugins { + kotlin("multiplatform.pm20").version("1.5.255-SNAPSHOT") + } +} + +rootProject.name = "kpmSensitivePluginOptions" \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kpmSensitivePluginOptions/src/jvmMain/kotlin/Foo.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kpmSensitivePluginOptions/src/jvmMain/kotlin/Foo.kt new file mode 100644 index 00000000000..093a55c5c30 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kpmSensitivePluginOptions/src/jvmMain/kotlin/Foo.kt @@ -0,0 +1,4 @@ +package testProject.kpmSensitivePluginOptions + +class Foo { +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/mpp/KpmCompilerPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/mpp/KpmCompilerPluginTest.kt similarity index 69% rename from libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/mpp/KpmCompilerPlugin.kt rename to libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/mpp/KpmCompilerPluginTest.kt index a00bcc02860..51327dce35a 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/mpp/KpmCompilerPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/mpp/KpmCompilerPluginTest.kt @@ -3,6 +3,7 @@ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ @file:Suppress("invisible_reference", "invisible_member", "FunctionName") + package org.jetbrains.kotlin.gradle.mpp import org.gradle.api.Project @@ -14,8 +15,7 @@ import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinNativeCompile import org.jetbrains.kotlin.gradle.tasks.KotlinCompilerPluginData import org.jetbrains.kotlin.project.model.* -import org.junit.Test -import kotlin.test.assertEquals +import kotlin.test.* class KpmCompilerPluginTest { @Test @@ -83,11 +83,46 @@ class KpmCompilerPluginTest { } } + @Test + fun `compiler plugins should be applied lazily`() { + val project = buildProjectWithKPM { + plugins.apply(TestPluginWithListeners::class.java) + + projectModel { + main { + jvm + } + } + } + + with(TestPluginWithListeners.Companion) { + onGetKpmCompilerPlugin = { throw AssertionError("KPM Compiler Plugin should not be obtained on project evaluate") } + onPluginDataGet = { throw AssertionError("Plugin data should not be requested on project evaluate") } + } + project.evaluate() + + // Getting task shouldn't trigger plugin initialization as well (lazy all the way) + val task = project.tasks.getByName("compileKotlinJvm") as AbstractKotlinCompile<*> + + var pluginInitialized = false + var pluginDataObtainCount = 0 + with(TestPluginWithListeners.Companion) { + onGetKpmCompilerPlugin = { pluginInitialized = true } + onPluginDataGet = { pluginDataObtainCount++ } + } + + // Upon pluginData request plugins should be initialized and obtained only once + repeat(5) { task.kotlinPluginData!!.get() } + + assertTrue(pluginInitialized) + assertEquals(1, pluginDataObtainCount) + } + private fun Project.pluginDataOfTask(taskName: String) = this .tasks .getByName(taskName) .let { - when(it) { + when (it) { is AbstractKotlinCompile<*> -> it.kotlinPluginData is AbstractKotlinNativeCompile<*, *> -> it.kotlinPluginData else -> error("Unknown task type: $it") @@ -128,4 +163,19 @@ class KpmCompilerPluginTest { override fun forPlatformCompilation(variant: KotlinModuleVariant) = platformArtifact?.let(::pluginData) } + + open class TestPluginWithListeners : KpmCompilerPlugin, GradleKpmCompilerPlugin { + override val kpmCompilerPlugin: KpmCompilerPlugin get() = this.also { onGetKpmCompilerPlugin() } + override fun apply(target: Project) = onApply() + override fun forMetadataCompilation(fragment: KotlinModuleFragment): PluginData? = null.also { onPluginDataGet() } + override fun forNativeMetadataCompilation(fragment: KotlinModuleFragment): PluginData? = null.also { onPluginDataGet() } + override fun forPlatformCompilation(variant: KotlinModuleVariant): PluginData? = null.also { onPluginDataGet() } + + companion object { + var onApply: () -> Unit = {} + var onGetKpmCompilerPlugin: () -> Unit = {} + var onPluginDataGet: () -> Unit = {} + } + } + } \ No newline at end of file