From 7f3970fb0fa11ad9e0f16512d3531448e31397e4 Mon Sep 17 00:00:00 2001 From: Sebastian Sellmair Date: Tue, 4 Jul 2023 13:00:10 +0200 Subject: [PATCH] [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 --- .../diagnostics/KotlinGradleProjectChecker.kt | 2 +- .../diagnostics/KotlinToolingDiagnostics.kt | 16 ++++++ .../KotlinToolingDiagnosticsCollector.kt | 9 ++-- .../gradle/targets/jvm/KotlinJvmTarget.kt | 25 ++++++++- ...IsIncompatibleWithAndroidDiagnosticTest.kt | 52 +++++++++++++++++++ ...ndroid-withJava-withAndroidApplication.txt | 8 +++ ...ithAndroid-withJava-withAndroidLibrary.txt | 8 +++ 7 files changed, 114 insertions(+), 6 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/diagnosticsTests/JvmWithJavaIsIncompatibleWithAndroidDiagnosticTest.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/expectedDiagnostics/JvmWithJavaIsIncompatibleWithAndroid-withJava-withAndroidApplication.txt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/expectedDiagnostics/JvmWithJavaIsIncompatibleWithAndroid-withJava-withAndroidLibrary.txt 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 148d388f298..8acc7f0623e 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 @@ internal interface KotlinGradleProjectChecker { 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 5ccf922f2ef..bddfb982337 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 @@ object KotlinToolingDiagnostics { """.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 f48c4d85a7e..4e728f02543 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 @@ internal abstract class KotlinToolingDiagnosticsCollector : BuildService 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 96bcbab8352..bc3802fc97c 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.NamedDomainObjectContainer 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.ExperimentalKotlinGradlePluginApi 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.targets.jvm.tasks.registerMainRunTask 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 @@ abstract class KotlinJvmTarget @Inject constructor( var withJavaEnabled = false private set + @Suppress("unused") // user DSL fun withJava() { if (withJavaEnabled) @@ -105,6 +107,25 @@ abstract class KotlinJvmTarget @Inject constructor( ) } + + /** + * 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 00000000000..5abce9ebfde --- /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 00000000000..aa8726f502c --- /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 00000000000..0c2ae7281fd --- /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 */ + } + }