From 732ba96667ceaa9233a0d9d9018c7bba7727d14b Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Fri, 16 Sep 2022 11:15:52 +0200 Subject: [PATCH] Add workaround for external plugin modifying options on task execution Currently, known plugins doing that are 'kotlin-dsl' and AGP with compose enabled. They are setting freeCompilerArgs in KotlinCompile .doFirst task action. At this point compilerOptions.freeCompilerArgs are finalized and Gradle doesn't allow further modification. ^KT-27301 In Progress --- .../kotlin/gradle/CompilerOptionsIT.kt | 29 ++++++++++++++ .../build.gradle.kts | 8 ++++ .../buildSrc/build.gradle.kts | 20 ++++++++++ .../buildSrc/src/main/kotlin/ProjectExt.kt | 8 ++++ .../simple-convention-plugin.gradle.kts | 3 ++ .../src/main/kotlin/main.kt | 8 ++++ .../jetbrains/kotlin/gradle/tasks/Tasks.kt | 23 +++++++++-- .../tasks/internal/KotlinJvmOptionsCompat.kt | 39 +++++++++++++++++++ 8 files changed, 134 insertions(+), 4 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/CompilerOptionsIT.kt create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/buildSrcWithKotlinDslAndKgp/build.gradle.kts create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/buildSrcWithKotlinDslAndKgp/buildSrc/build.gradle.kts create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/buildSrcWithKotlinDslAndKgp/buildSrc/src/main/kotlin/ProjectExt.kt create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/buildSrcWithKotlinDslAndKgp/buildSrc/src/main/kotlin/simple-convention-plugin.gradle.kts create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/buildSrcWithKotlinDslAndKgp/src/main/kotlin/main.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/internal/KotlinJvmOptionsCompat.kt diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/CompilerOptionsIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/CompilerOptionsIT.kt new file mode 100644 index 00000000000..b985af03b1e --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/CompilerOptionsIT.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2010-2022 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.gradle.util.GradleVersion +import org.jetbrains.kotlin.gradle.testbase.* +import org.junit.jupiter.api.DisplayName + +internal class CompilerOptionsIT : KGPBaseTest() { + + // In Gradle 7.3-7.5 'kotlin-dsl' plugin tries to set up freeCompilerArgs in doFirst task action + @DisplayName("Allows to set kotlinOptions.freeCompilerArgs on task execution with warning") + @JvmGradlePluginTests + @GradleTestVersions( + minVersion = TestVersions.Gradle.G_7_3, + maxVersion = TestVersions.Gradle.G_7_5 + ) + @GradleTest + internal fun compatibleWithKotlinDsl(gradleVersion: GradleVersion) { + project("buildSrcWithKotlinDslAndKgp", gradleVersion) { + build("tasks") { + assertOutputContains("kotlinOptions.freeCompilerArgs were changed on task execution phase:") + } + } + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/buildSrcWithKotlinDslAndKgp/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/buildSrcWithKotlinDslAndKgp/build.gradle.kts new file mode 100644 index 00000000000..274be350f5a --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/buildSrcWithKotlinDslAndKgp/build.gradle.kts @@ -0,0 +1,8 @@ +plugins { + kotlin("jvm") +} + +repositories { + mavenLocal() + mavenCentral() +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/buildSrcWithKotlinDslAndKgp/buildSrc/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/buildSrcWithKotlinDslAndKgp/buildSrc/build.gradle.kts new file mode 100644 index 00000000000..9b2cc16a881 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/buildSrcWithKotlinDslAndKgp/buildSrc/build.gradle.kts @@ -0,0 +1,20 @@ +//buildscript { +// dependencies { +// classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${property("kotlin_version")}") +// classpath("org.jetbrains.kotlin:kotlin-sam-with-receiver:${property("kotlin_version")}") +// } +//} + +plugins { + id("org.jetbrains.kotlin.jvm") + `kotlin-dsl` +} + +repositories { + mavenLocal() + mavenCentral() +} + +dependencies { + implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:${property("kotlin_version")}") +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/buildSrcWithKotlinDslAndKgp/buildSrc/src/main/kotlin/ProjectExt.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/buildSrcWithKotlinDslAndKgp/buildSrc/src/main/kotlin/ProjectExt.kt new file mode 100644 index 00000000000..32ecc13b646 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/buildSrcWithKotlinDslAndKgp/buildSrc/src/main/kotlin/ProjectExt.kt @@ -0,0 +1,8 @@ +/* + * Copyright 2010-2022 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 org.gradle.api.Project + +fun Project.nameLength() = name.length diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/buildSrcWithKotlinDslAndKgp/buildSrc/src/main/kotlin/simple-convention-plugin.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/buildSrcWithKotlinDslAndKgp/buildSrc/src/main/kotlin/simple-convention-plugin.gradle.kts new file mode 100644 index 00000000000..e8c1f3ff412 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/buildSrcWithKotlinDslAndKgp/buildSrc/src/main/kotlin/simple-convention-plugin.gradle.kts @@ -0,0 +1,3 @@ +dependencies { + "implementation"("org.jetbrains.kotlin:kotlin-symbol-processing-api") +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/buildSrcWithKotlinDslAndKgp/src/main/kotlin/main.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/buildSrcWithKotlinDslAndKgp/src/main/kotlin/main.kt new file mode 100644 index 00000000000..0d61647bc70 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/buildSrcWithKotlinDslAndKgp/src/main/kotlin/main.kt @@ -0,0 +1,8 @@ +/* + * Copyright 2010-2022 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() { + println("Hello World!") +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt index 80b5aa3a5d4..324d0af2260 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt @@ -56,6 +56,7 @@ import org.jetbrains.kotlin.gradle.report.BuildMetricsService import org.jetbrains.kotlin.gradle.report.BuildReportMode import org.jetbrains.kotlin.gradle.report.BuildReportsService import org.jetbrains.kotlin.gradle.report.ReportingSettings +import org.jetbrains.kotlin.gradle.tasks.internal.KotlinJvmOptionsCompat import org.jetbrains.kotlin.gradle.utils.* import org.jetbrains.kotlin.incremental.* import org.jetbrains.kotlin.incremental.ClasspathChanges.ClasspathSnapshotDisabled @@ -535,10 +536,10 @@ abstract class KotlinCompile @Inject constructor( @Suppress("DEPRECATION") @Deprecated("Replaced by compilerOptions input", replaceWith = ReplaceWith("compilerOptions")) - final override val kotlinOptions: KotlinJvmOptions = object : KotlinJvmOptions { - override val options: CompilerJvmOptions - get() = compilerOptions - } + final override val kotlinOptions: KotlinJvmOptions = KotlinJvmOptionsCompat( + { this }, + compilerOptions + ) @Suppress("DEPRECATION") @Deprecated("Configure compilerOptions directly", replaceWith = ReplaceWith("compilerOptions")) @@ -677,6 +678,16 @@ abstract class KotlinCompile @Inject constructor( override fun createCompilerArgs(): K2JVMCompilerArguments = K2JVMCompilerArguments() + /** + * Workaround for those "nasty" plugins that are adding 'freeCompilerArgs' on task execution phase. + * With properties api it is not possible to update property value after task configuration is finished. + * + * Marking it as `@Internal` as anyway on the configuration phase, when Gradle does task inputs snapshot, + * this input will always be empty. + */ + @get:Internal + internal var additionalFreeCompilerArgs: List = listOf() + override fun setupCompilerArgs(args: K2JVMCompilerArguments, defaultsOnly: Boolean, ignoreClasspathResolutionErrors: Boolean) { compilerArgumentsContributor.contributeArguments( args, compilerArgumentsConfigurationFlags( @@ -690,6 +701,10 @@ abstract class KotlinCompile @Inject constructor( if (reportingSettings().buildReportMode == BuildReportMode.VERBOSE) { args.reportPerf = true } + + if (additionalFreeCompilerArgs.isNotEmpty()) { + args.freeArgs = compilerOptions.freeCompilerArgs.get().union(additionalFreeCompilerArgs).toList() + } } @get:Internal diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/internal/KotlinJvmOptionsCompat.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/internal/KotlinJvmOptionsCompat.kt new file mode 100644 index 00000000000..ae05e1ef7a5 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/internal/KotlinJvmOptionsCompat.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2010-2022 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.tasks.internal + +import org.jetbrains.kotlin.gradle.dsl.* +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +/** + * Temporary workaround for external plugins that tries to set up freeCompilerArgs + * in task execution phase. + */ +@Suppress("DEPRECATION") +class KotlinJvmOptionsCompat( + private val task: () -> KotlinCompile, + override val options: CompilerJvmOptions +) : KotlinJvmOptions { + override var freeCompilerArgs: List + get() = if (isTaskExecuting) { + options.freeCompilerArgs.get().union(task().additionalFreeCompilerArgs).toList() + } else { + options.freeCompilerArgs.get() + } + + set(value) = if (isTaskExecuting) { + task().logger.warn( + "kotlinOptions.freeCompilerArgs were changed on task execution phase: ${value.joinToString()}\n" + + "This behaviour is deprecated and become an error in future releases!" + ) + task().additionalFreeCompilerArgs = value + } else { + options.freeCompilerArgs.set(value) + } + + private val isTaskExecuting: Boolean + get() = task().state.executing +}