Warning for Kotlin plugins loaded multiple times (KT-30276)

Print a warning with an instruction on how to fix this. Don't warn about
different versions and duplicate plugins in composite build.

Issue #KT-30276 Fixed
This commit is contained in:
Sergey Igushkin
2019-03-04 18:08:18 +03:00
parent 42a7c87e2e
commit 7d9405a9cb
4 changed files with 129 additions and 7 deletions
@@ -17,6 +17,8 @@
package org.jetbrains.kotlin.gradle
import org.gradle.api.logging.LogLevel
import org.jetbrains.kotlin.gradle.plugin.MULTIPLE_KOTLIN_PLUGINS_LOADED_WARNING
import org.jetbrains.kotlin.gradle.plugin.MULTIPLE_KOTLIN_PLUGINS_SPECIFIC_PROJECTS_WARNING
import org.jetbrains.kotlin.gradle.tasks.USING_JVM_INCREMENTAL_COMPILATION_MESSAGE
import org.jetbrains.kotlin.gradle.util.*
import org.junit.Test
@@ -823,4 +825,70 @@ class KotlinGradleIT : BaseGradleIT() {
assertTasksExecuted(":jvm-app:compileKotlin")
}
}
@Test
fun testDetectingDifferentClassLoaders() = with(Project("kt-27059-pom-rewriting", GradleVersionRequired.AtLeast("4.10.2"))) {
setupWorkingDir()
val originalRootBuildScript = gradleBuildScript().readText()
gradleBuildScript().modify(::transformBuildScriptWithPluginsDsl)
build("publish", "-PmppProjectDependency=true") {
assertSuccessful()
assertNotContains(MULTIPLE_KOTLIN_PLUGINS_LOADED_WARNING)
assertNotContains(MULTIPLE_KOTLIN_PLUGINS_SPECIFIC_PROJECTS_WARNING)
}
// Specify the plugin versions in the subprojects with different plugin sets this will make Gradle use separate class loaders
gradleBuildScript().modify {
originalRootBuildScript.checkedReplace("id \"org.jetbrains.kotlin.multiplatform\"", "//")
}
gradleBuildScript("mpp-lib").modify {
it.checkedReplace(
"id \"org.jetbrains.kotlin.multiplatform\"",
"id \"org.jetbrains.kotlin.multiplatform\" version \"<pluginMarkerVersion>\""
).let(::transformBuildScriptWithPluginsDsl)
}
gradleBuildScript("jvm-app").modify {
it.checkedReplace(
"id \"org.jetbrains.kotlin.jvm\"",
"id \"org.jetbrains.kotlin.jvm\" version \"<pluginMarkerVersion>\""
).let(::transformBuildScriptWithPluginsDsl)
}
gradleBuildScript("js-app").modify {
it.checkedReplace(
"id \"kotlin2js\"",
"id \"kotlin2js\" version \"<pluginMarkerVersion>\""
).let(::transformBuildScriptWithPluginsDsl)
}
// Also include another project via a composite build:
transformProjectWithPluginsDsl("allopenPluginsDsl", directoryPrefix = "pluginsDsl").let { other ->
val result = other.projectName
other.setupWorkingDir()
other.projectDir.copyRecursively(projectDir.resolve(result))
gradleSettingsScript().appendText("\nincludeBuild(\"${result}\")")
gradleBuildScript().appendText(
"\ntasks.create(\"publish\").dependsOn(gradle.includedBuild(\"${result}\").task(\":assemble\"))"
)
result
}
build("publish", "-PmppProjectDependency=true") {
assertSuccessful()
assertContains(MULTIPLE_KOTLIN_PLUGINS_LOADED_WARNING)
val specificProjectsReported = Regex("$MULTIPLE_KOTLIN_PLUGINS_SPECIFIC_PROJECTS_WARNING((?:'.*'(?:, )?)+)")
.find(output)!!.groupValues[1].split(", ").map { it.removeSurrounding("'") }.toSet()
assertEquals(setOf(":mpp-lib", ":jvm-app", ":js-app"), specificProjectsReported)
}
// Test the flag that turns off the warnings
build("publish", "-PmppProjectDependency=true", "-Pkotlin.pluginLoadedInMultipleProjects.ignore=true") {
assertSuccessful()
assertNotContains(MULTIPLE_KOTLIN_PLUGINS_LOADED_WARNING)
assertNotContains(MULTIPLE_KOTLIN_PLUGINS_SPECIFIC_PROJECTS_WARNING)
}
}
}
@@ -18,23 +18,19 @@ package org.jetbrains.kotlin.gradle.plugin
import org.gradle.BuildAdapter
import org.gradle.BuildResult
import org.gradle.api.Project
import org.gradle.api.invocation.Gradle
import org.gradle.api.logging.Logger
import org.gradle.api.logging.Logging
import org.gradle.api.plugins.ExtraPropertiesExtension
import org.jetbrains.kotlin.compilerRunner.DELETED_SESSION_FILE_PREFIX
import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner
import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskLoggers
import org.jetbrains.kotlin.gradle.logging.kotlinDebug
import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskExecutionResults
import org.jetbrains.kotlin.gradle.report.KotlinBuildReporter
import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskLoggers
import org.jetbrains.kotlin.gradle.report.configureBuildReporter
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
import org.jetbrains.kotlin.gradle.utils.relativeToRoot
import org.jetbrains.kotlin.utils.addToStdlib.sumByLong
import java.io.File
import java.lang.management.ManagementFactory
import java.text.SimpleDateFormat
import java.util.*
internal class KotlinGradleBuildServices private constructor(
private val gradle: Gradle
@@ -137,5 +133,58 @@ internal class KotlinGradleBuildServices private constructor(
private fun getGcCount(): Long =
ManagementFactory.getGarbageCollectorMXBeans().sumByLong { Math.max(0, it.collectionCount) }
private var loadedInProjectPath: String? = null
@Synchronized
internal fun detectKotlinPluginLoadedInMultipleProjects(project: Project, kotlinPluginVersion: String) {
val projectPath = project.path
val loadedInProjectsPropertyName = "kotlin.plugin.loaded.in.projects.${kotlinPluginVersion}"
if (loadedInProjectPath == null) {
loadedInProjectPath = projectPath
val ext = project.rootProject.extensions.getByType(ExtraPropertiesExtension::class.java)
if (!ext.has(loadedInProjectsPropertyName)) {
ext.set(loadedInProjectsPropertyName, projectPath)
gradle.taskGraph.whenReady {
val loadedInProjects = (ext.get(loadedInProjectsPropertyName) as String).split(";")
if (loadedInProjects.size > 1) {
if (PropertiesProvider(project).ignorePluginLoadedInMultipleProjects != true) {
project.logger.warn(MULTIPLE_KOTLIN_PLUGINS_LOADED_WARNING)
project.logger.warn(
MULTIPLE_KOTLIN_PLUGINS_SPECIFIC_PROJECTS_WARNING + loadedInProjects.joinToString(limit = 4) { "'$it'" }
)
}
project.logger.info(
"$MULTIPLE_KOTLIN_PLUGINS_SPECIFIC_PROJECTS_INFO: " +
loadedInProjects.joinToString { "'$it'" }
)
}
}
} else {
ext.set(loadedInProjectsPropertyName, (ext.get(loadedInProjectsPropertyName) as String) + ";" + loadedInProjectPath)
}
}
}
}
const val MULTIPLE_KOTLIN_PLUGINS_LOADED_WARNING: String =
"\nThe Kotlin Gradle plugin was loaded multiple times in different subprojects, which is not supported and may break the build. \n" +
"This might happen in subprojects that apply the Kotlin plugins with the Gradle 'plugins { ... }' DSL if they specify " +
"explicit versions, even if the versions are equal.\n" +
"Please add the Kotlin plugin to the common parent project or the root project, then remove the versions in the subprojects.\n" +
"If the parent project does not need the plugin, add 'apply false' to the plugin line.\n" +
"See: https://docs.gradle.org/current/userguide/plugins.html#sec:subprojects_plugins_dsl"
const val MULTIPLE_KOTLIN_PLUGINS_SPECIFIC_PROJECTS_WARNING: String =
"The Kotlin plugin was loaded in the following projects: "
const val MULTIPLE_KOTLIN_PLUGINS_SPECIFIC_PROJECTS_INFO: String = "The full list of projects that loaded the Kotlin plugin is: "
@@ -67,6 +67,8 @@ abstract class KotlinBasePluginWrapper(
System.setProperty(org.jetbrains.kotlin.cli.common.KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY, "true")
val kotlinGradleBuildServices = KotlinGradleBuildServices.getInstance(project.gradle)
kotlinGradleBuildServices.detectKotlinPluginLoadedInMultipleProjects(project, kotlinPluginVersion)
project.createKotlinExtension(projectExtensionClass).apply {
fun kotlinSourceSetContainer(factory: NamedDomainObjectFactory<KotlinSourceSet>) =
project.container(KotlinSourceSet::class.java, factory)
@@ -82,6 +82,9 @@ internal class PropertiesProvider(private val project: Project) {
val keepMppDependenciesIntactInPoms: Boolean?
get() = booleanProperty("kotlin.mpp.keepMppDependenciesIntactInPoms")
val ignorePluginLoadedInMultipleProjects: Boolean?
get() = booleanProperty("kotlin.pluginLoadedInMultipleProjects.ignore")
/**
* Enables parallel tasks execution within a project with Workers API.
* Does not enable using actual worker proccesses