Reorder Gradle -Xplugin=... so that serialization goes first (KT-47921)

Ensure that the serialization plugin's artifact (and, possibly, some
other prioritized plugin artifacts) are put first to the -Xplugin=...
classpath passed to the compiler.

This fixes the conflict of the serialization plugin with some other
plugins altering the IR in unexpected ways.

Issue #KT-47921
This commit is contained in:
Sergey Igushkin
2021-07-29 16:55:29 +04:00
parent 033a9f1589
commit edaf925b19
8 changed files with 71 additions and 8 deletions
@@ -275,6 +275,30 @@ class SubpluginsIT : BaseGradleIT() {
Project("lombokProject").build("build") {
assertSuccessful()
}
}
@Test // KT-47921
fun testSerializationPluginOrderedFirst() = with(Project("allOpenSimple")) {
setupWorkingDir()
// Ensure that there are also allopen, noarg, and serialization plugins applied:
gradleBuildScript().appendText(
"""
buildscript {
dependencies {
classpath("org.jetbrains.kotlin:kotlin-noarg:${defaultBuildOptions().kotlinVersion}")
classpath("org.jetbrains.kotlin:kotlin-serialization:${defaultBuildOptions().kotlinVersion}")
}
}
apply plugin: "org.jetbrains.kotlin.plugin.noarg"
apply plugin: "org.jetbrains.kotlin.plugin.serialization"
""".trimIndent()
)
build("compileKotlin") {
assertSuccessful()
val xPlugin = output.split(" ").single { it.startsWith("-Xplugin") }.substringAfter("-Xplugin").split(",")
assertTrue("Expected serialization plugin to go first; actual order: $xPlugin") { xPlugin.first().contains("serialization") }
}
}
}
@@ -14,7 +14,7 @@ import org.jetbrains.kotlin.gradle.logging.kotlinDebug
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinCompileArgumentsProvider
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompilerArgumentsProvider
import org.jetbrains.kotlin.gradle.utils.toSortedPathsArray
import org.jetbrains.kotlin.gradle.utils.toPathsArray
import org.jetbrains.kotlin.incremental.classpathAsList
import org.jetbrains.kotlin.incremental.destinationAsFile
@@ -63,7 +63,7 @@ internal open class AbstractKotlinCompileArgumentsContributor<T : CommonCompiler
}
internal fun setupPlugins(compilerArgs: T) {
compilerArgs.pluginClasspaths = pluginClasspath.toSortedPathsArray()
compilerArgs.pluginClasspaths = pluginClasspath.toPathsArray()
compilerArgs.pluginOptions = pluginOptions.arguments.toTypedArray()
}
}
@@ -0,0 +1,35 @@
/*
* Copyright 2010-2021 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.internal
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.ModuleDependency
/**
* Reorder the compiler plugin dependencies in this [Configuration] so that dependencies on the
* modules under [prioritizedPluginArtifactCoordinates] appear first.
*
* In particular, the serialization plugin needs to appear first on the -Xplugin classpath in order to avoid conflicts with other compiler
* plugins producing unexpected IR.
*
* KT-47921
*/
internal fun Configuration.reorderPluginClasspathDependencies() {
withDependencies { dependencySet ->
val orderedDependencies = dependencySet.toList().partition(Dependency::isPrioritized).toList().flatten()
dependencySet.clear()
dependencySet.addAll(orderedDependencies)
}
}
private val Dependency.isPrioritized: Boolean
get() = this is ModuleDependency && (group to name) in prioritizedPluginArtifactCoordinates
private val prioritizedPluginArtifactCoordinates = setOf(
"org.jetbrains.kotlin" to "kotlin-serialization",
"org.jetbrains.kotlin" to "kotlin-serialization-unshaded"
)
@@ -24,7 +24,7 @@ import org.jetbrains.kotlin.gradle.report.ReportingSettings
import org.jetbrains.kotlin.gradle.tasks.*
import org.jetbrains.kotlin.gradle.utils.newInstance
import org.jetbrains.kotlin.gradle.utils.property
import org.jetbrains.kotlin.gradle.utils.toSortedPathsArray
import org.jetbrains.kotlin.gradle.utils.toPathsArray
import java.io.File
import javax.inject.Inject
@@ -71,7 +71,7 @@ abstract class KaptWithKotlincTask @Inject constructor(
ignoreClasspathResolutionErrors
))
args.pluginClasspaths = pluginClasspath.toSortedPathsArray()
args.pluginClasspaths = pluginClasspath.toPathsArray()
val pluginOptionsWithKapt: CompilerPluginOptions = pluginOptions.withWrappedKaptOptions(
withApClasspath = kaptClasspath,
@@ -27,6 +27,7 @@ import org.gradle.language.base.plugins.LifecycleBasePlugin
import org.gradle.language.jvm.tasks.ProcessResources
import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.internal.reorderPluginClasspathDependencies
import org.jetbrains.kotlin.gradle.plugin.mpp.*
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsCompilerAttribute
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget
@@ -286,6 +287,7 @@ abstract class AbstractKotlinTargetConfigurator<KotlinTargetType : KotlinTarget>
isVisible = false
isCanBeConsumed = false
description = "Kotlin compiler plugins for $compilation"
reorderPluginClasspathDependencies()
}
val compileConfiguration = configurations.findByName(compilation.deprecatedCompileConfigurationName)?.apply {
@@ -7,9 +7,9 @@ package org.jetbrains.kotlin.gradle.plugin.mpp.pm20
import org.gradle.api.Project
import org.gradle.api.provider.Provider
import org.jetbrains.kotlin.gradle.internal.reorderPluginClasspathDependencies
import org.jetbrains.kotlin.gradle.plugin.FilesSubpluginOption
import org.jetbrains.kotlin.gradle.plugin.SubpluginOption
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
import org.jetbrains.kotlin.gradle.tasks.CompilerPluginOptions
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilerPluginData
import org.jetbrains.kotlin.gradle.utils.newProperty
@@ -93,6 +93,7 @@ internal class CompilerPluginOptionsBuilder(
isCanBeConsumed = false
isCanBeResolved = true
isVisible = false
reorderPluginClasspathDependencies()
}
artifacts.forEach { project.dependencies.add(configurationName, it) }
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.gradle.tasks
import org.gradle.api.GradleException
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.Configuration
import org.gradle.api.file.*
import org.gradle.api.invocation.Gradle
import org.gradle.api.attributes.Attribute
@@ -26,8 +26,8 @@ internal fun Iterable<File>.pathsAsStringRelativeTo(base: File): String =
internal fun File.relativeToRoot(project: Project): String =
relativeOrCanonical(project.rootProject.rootDir)
internal fun Iterable<File>.toSortedPathsArray(): Array<String> =
map { it.canonicalPath }.toTypedArray().also { Arrays.sort(it) }
internal fun Iterable<File>.toPathsArray(): Array<String> =
map { it.canonicalPath }.toTypedArray()
internal fun newTmpFile(prefix: String, suffix: String? = null, directory: File? = null, deleteOnExit: Boolean = true): File =
(if (directory == null) Files.createTempFile(prefix, suffix) else Files.createTempFile(directory.toPath(), prefix, suffix))