[Gradle] Implement KotlinJvmWithJavaAndroidCompatibilityChecker to fail when 'withJava' is used together with AGP As both (AGP and 'withJava') are using project global names like 'main' and 'test' which will lead to clashes (e.g. configuration names are the same for both) ^KT-59595 Verification Pending
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinGradleProjectChecker.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinGradleProjectChecker.kt index 148d388..8acc7f0 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinGradleProjectChecker.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinGradleProjectChecker.kt
@@ -113,7 +113,7 @@ KotlinSourceSetTreeDependsOnMismatchChecker, PlatformSourceSetConventionsChecker, AndroidMainSourceSetConventionsChecker, - IosSourceSetConventionChecker + IosSourceSetConventionChecker, ) } }
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnostics.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnostics.kt index 5ccf922..bddfb98 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnostics.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnostics.kt
@@ -530,6 +530,22 @@ """.trimIndent() ) } + + object JvmWithJavaIsIncompatibleWithAndroid : ToolingDiagnosticFactory(FATAL) { + operator fun invoke(androidPluginId: String, trace: Throwable?) = build( + """ + 'withJava()' is not compatible with Android Plugins + Incompatible Android Plugin applied: '$androidPluginId' + + kotlin { + jvm { + withJava() /* <- cannot be used when the Android Plugin is present */ + } + } + """.trimIndent(), + throwable = trace + ) + } } private fun String.indentLines(nSpaces: Int = 4, skipFirstLine: Boolean = true): String {
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnosticsCollector.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnosticsCollector.kt index f48c4d8..4e728f0 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnosticsCollector.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnosticsCollector.kt
@@ -68,12 +68,15 @@ return } - if (diagnostic.severity == ToolingDiagnostic.Severity.FATAL) { - throw InvalidUserCodeException(diagnostic.message) - } rawDiagnosticsFromProject.compute(project.path) { _, previousListIfAny -> previousListIfAny?.apply { add(diagnostic) } ?: mutableListOf(diagnostic) } + + if (diagnostic.severity == ToolingDiagnostic.Severity.FATAL) { + if (diagnostic.throwable != null) + throw InvalidUserCodeException(diagnostic.message, diagnostic.throwable) + else throw InvalidUserCodeException(diagnostic.message) + } } }
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmTarget.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmTarget.kt index 96bcbab..bc3802f 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmTarget.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmTarget.kt
@@ -10,9 +10,7 @@ import org.gradle.api.Project import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.DuplicatesStrategy -import org.gradle.api.internal.tasks.JvmConstants import org.gradle.api.plugins.JavaBasePlugin -import org.gradle.api.plugins.JavaPlugin import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.compile.AbstractCompile import org.gradle.language.jvm.tasks.ProcessResources @@ -21,6 +19,8 @@ import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycle.Stage.AfterFinaliseDsl +import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinToolingDiagnostics +import org.jetbrains.kotlin.gradle.plugin.diagnostics.reportDiagnostic import org.jetbrains.kotlin.gradle.plugin.internal.JavaSourceSetsAccessor import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmCompilation import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinOnlyTarget @@ -32,6 +32,7 @@ import org.jetbrains.kotlin.gradle.tasks.withType import org.jetbrains.kotlin.gradle.utils.Future import org.jetbrains.kotlin.gradle.utils.addExtendsFromRelation +import org.jetbrains.kotlin.gradle.utils.findAppliedAndroidPluginIdOrNull import org.jetbrains.kotlin.gradle.utils.future import org.jetbrains.kotlin.utils.addToStdlib.cast import java.util.concurrent.Callable @@ -92,6 +93,7 @@ var withJavaEnabled = false private set + @Suppress("unused") // user DSL fun withJava() { if (withJavaEnabled) @@ -105,6 +107,25 @@ ) } + + /** + * Reports diagnostic in the case of + * ```kotlin + * kotlin { + * jvm().withJava() + * } + * ``` + * + * is used together with the Android Gradle Plugin. + * This case is incompatible so far, as the 'withJava' implementation is still using 'global' namespaces + * (like main/test, etc), which will clash with the global names used by AGP (also occupying main, test, etc). + */ + val trace = Throwable() + project.launchInStage(AfterFinaliseDsl) check@{ + val androidPluginId = project.findAppliedAndroidPluginIdOrNull() ?: return@check + project.reportDiagnostic(KotlinToolingDiagnostics.JvmWithJavaIsIncompatibleWithAndroid(androidPluginId, trace)) + } + withJavaEnabled = true project.plugins.apply(JavaBasePlugin::class.java)
diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/diagnosticsTests/JvmWithJavaIsIncompatibleWithAndroidDiagnosticTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/diagnosticsTests/JvmWithJavaIsIncompatibleWithAndroidDiagnosticTest.kt new file mode 100644 index 0000000..5abce9e --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/diagnosticsTests/JvmWithJavaIsIncompatibleWithAndroidDiagnosticTest.kt
@@ -0,0 +1,52 @@ +/* + * Copyright 2010-2023 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. + */ + +@file:Suppress("FunctionName") + +package org.jetbrains.kotlin.gradle.unitTests.diagnosticsTests + +import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension +import org.jetbrains.kotlin.gradle.util.androidApplication +import org.jetbrains.kotlin.gradle.util.androidLibrary +import org.jetbrains.kotlin.gradle.util.buildProjectWithMPP +import org.jetbrains.kotlin.gradle.util.checkDiagnostics +import kotlin.test.Test +import kotlin.test.assertFails + +class JvmWithJavaIsIncompatibleWithAndroidDiagnosticTest { + + @Test + fun `test - withJava and android library`() { + val project = buildProjectWithMPP() + project.androidLibrary { compileSdk = 33 } + project.multiplatformExtension.jvm().withJava() + project.multiplatformExtension.androidTarget() + + assertFails { project.evaluate() } + project.checkDiagnostics("JvmWithJavaIsIncompatibleWithAndroid-withJava-withAndroidLibrary") + } + + @Test + fun `test - withJava and android application`() { + val project = buildProjectWithMPP() + project.androidApplication { compileSdk = 33 } + project.multiplatformExtension.jvm().withJava() + project.multiplatformExtension.androidTarget() + assertFails { project.evaluate() } + + project.checkDiagnostics("JvmWithJavaIsIncompatibleWithAndroid-withJava-withAndroidApplication") + } + + @Test + fun `test - withJava - without android plugin`() { + val project = buildProjectWithMPP() + project.androidLibrary { compileSdk = 33 } + project.multiplatformExtension.jvm() + project.multiplatformExtension.androidTarget() + project.evaluate() + + project.checkDiagnostics("JvmWithJavaIsIncompatibleWithAndroid-withJava-withoutAndroidPlugin") + } +} \ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/expectedDiagnostics/JvmWithJavaIsIncompatibleWithAndroid-withJava-withAndroidApplication.txt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/expectedDiagnostics/JvmWithJavaIsIncompatibleWithAndroid-withJava-withAndroidApplication.txt new file mode 100644 index 0000000..aa8726f --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/expectedDiagnostics/JvmWithJavaIsIncompatibleWithAndroid-withJava-withAndroidApplication.txt
@@ -0,0 +1,8 @@ +[JvmWithJavaIsIncompatibleWithAndroid | FATAL] 'withJava()' is not compatible with Android Plugins +Incompatible Android Plugin applied: 'com.android.application' + + kotlin { + jvm { + withJava() /* <- cannot be used when the Android Plugin is present */ + } + }
diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/expectedDiagnostics/JvmWithJavaIsIncompatibleWithAndroid-withJava-withAndroidLibrary.txt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/expectedDiagnostics/JvmWithJavaIsIncompatibleWithAndroid-withJava-withAndroidLibrary.txt new file mode 100644 index 0000000..0c2ae72 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/expectedDiagnostics/JvmWithJavaIsIncompatibleWithAndroid-withJava-withAndroidLibrary.txt
@@ -0,0 +1,8 @@ +[JvmWithJavaIsIncompatibleWithAndroid | FATAL] 'withJava()' is not compatible with Android Plugins +Incompatible Android Plugin applied: 'com.android.library' + + kotlin { + jvm { + withJava() /* <- cannot be used when the Android Plugin is present */ + } + }