Add separate Plugin Classpaths per compilation
Make them extend the common CP for sake of backward compatibility ^KT-45020 Fixed
This commit is contained in:
+185
@@ -0,0 +1,185 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
@file:Suppress("invisible_reference", "invisible_member", "FunctionName")
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.gradle.mpp
|
||||||
|
|
||||||
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.api.internal.project.ProjectInternal
|
||||||
|
import org.gradle.api.provider.Provider
|
||||||
|
import org.gradle.testfixtures.ProjectBuilder
|
||||||
|
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
|
||||||
|
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.*
|
||||||
|
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertNotNull
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
internal class CompilationSpecificPluginPath {
|
||||||
|
@Test
|
||||||
|
fun `each compilation should have its own plugin classpath`() {
|
||||||
|
val project = buildProjectWithMPP {
|
||||||
|
kotlin {
|
||||||
|
jvm("jvm1")
|
||||||
|
jvm("jvm2")
|
||||||
|
js("js") { browser() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expect to see plugin classpath for each target compilation
|
||||||
|
listOf("jvm1", "jvm2", "js")
|
||||||
|
.map(String::capitalize)
|
||||||
|
.flatMap { listOf(pluginClassPathConfiguration(it, "main"), pluginClassPathConfiguration(it, "test")) }
|
||||||
|
.plus(pluginClassPathConfiguration("metadata", "main")) // and also one for metadata
|
||||||
|
.forEach { assertNotNull(project.configurations.findByName(it), "Configuration $it should exist") }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `kotlin plugin author can control plugin artifacts per compilation`() {
|
||||||
|
// Given platform-specific Kotlin plugins
|
||||||
|
class JvmOnly : FakeSubPlugin("jvmOnly", null, { target.platformType == KotlinPlatformType.jvm })
|
||||||
|
class JsOnly : FakeSubPlugin("jsOnly", null, { target.platformType == KotlinPlatformType.js })
|
||||||
|
|
||||||
|
// When apply them to a Kotlin MPP Project
|
||||||
|
val project = buildProjectWithMPP {
|
||||||
|
plugins.apply(JvmOnly::class.java)
|
||||||
|
plugins.apply(JsOnly::class.java)
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
jvm("desktop")
|
||||||
|
js("web", IR) {
|
||||||
|
browser()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
project.evaluate()
|
||||||
|
|
||||||
|
// Then each plugin classpath should have its own dependency
|
||||||
|
assertEquals(listOf("kotlin-scripting-compiler-embeddable", "jvmOnly"), project.subplugins("desktop"))
|
||||||
|
assertEquals(listOf("kotlin-scripting-compiler-embeddable", "jsOnly"), project.subplugins("web"))
|
||||||
|
|
||||||
|
// And each compilation task should have its own plugin classpath
|
||||||
|
val compileDesktop = project.tasks.getByName("compileKotlinDesktop") as KotlinCompile
|
||||||
|
val expectedConfig = project.configurations.getByName(pluginClassPathConfiguration("desktop", "main"))
|
||||||
|
assertEquals(expectedConfig, compileDesktop.pluginClasspath)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `native artifact should take precedence over regular artifact for native platform`() {
|
||||||
|
// Given common plugin but with native-specific artifact
|
||||||
|
class NativeSpecificPlugin : FakeSubPlugin("common", "native", { true })
|
||||||
|
|
||||||
|
val project = buildProjectWithMPP {
|
||||||
|
plugins.apply(NativeSpecificPlugin::class.java)
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
jvm()
|
||||||
|
linuxX64()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
project.evaluate()
|
||||||
|
|
||||||
|
assertTrue("common" in project.subplugins("jvm"))
|
||||||
|
assertTrue("native" in project.subplugins("linuxX64"))
|
||||||
|
assertTrue("common" !in project.subplugins("linuxX64"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it should be possible to add common plugin classpath to all compilations except native`() {
|
||||||
|
// Given MPP project
|
||||||
|
val project = buildProjectWithMPP {
|
||||||
|
kotlin {
|
||||||
|
jvm()
|
||||||
|
js { browser() }
|
||||||
|
linuxX64("native")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// When adding a shared plugin to common Plugin Classpath
|
||||||
|
project.dependencies.add(PLUGIN_CLASSPATH_CONFIGURATION_NAME, "test:shared")
|
||||||
|
project.evaluate()
|
||||||
|
|
||||||
|
// Then ALL compilations should have shared plugin EXCEPT native
|
||||||
|
assertTrue("shared" in project.subplugins("jvm"))
|
||||||
|
assertTrue("shared" in project.subplugins("js"))
|
||||||
|
assertTrue("shared" in project.subplugins("js", "test"))
|
||||||
|
assertTrue("shared" in project.subplugins("metadata"))
|
||||||
|
assertTrue("shared" !in project.subplugins("native"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `native compilations should have its own shared plugin classpath`() {
|
||||||
|
// Given MPP project
|
||||||
|
val project = buildProjectWithMPP {
|
||||||
|
kotlin {
|
||||||
|
jvm()
|
||||||
|
linuxX64("linux")
|
||||||
|
linuxX64("mac")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// When adding a shared plugin to native Plugin Classpath
|
||||||
|
project.dependencies.add(NATIVE_COMPILER_PLUGIN_CLASSPATH_CONFIGURATION_NAME, "test:shared")
|
||||||
|
project.evaluate()
|
||||||
|
|
||||||
|
// Then ALL native compilations should have shared plugin
|
||||||
|
assertTrue("shared" in project.subplugins("linux"))
|
||||||
|
assertTrue("shared" in project.subplugins("mac"))
|
||||||
|
assertTrue("shared" in project.subplugins("mac", "test"))
|
||||||
|
|
||||||
|
// And all non-native should not have it
|
||||||
|
assertTrue("shared" !in project.subplugins("metadata"))
|
||||||
|
assertTrue("shared" !in project.subplugins("jvm"))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun pluginClassPathConfiguration(target: String, compilation: String) =
|
||||||
|
"kotlinCompilerPluginClasspath${target.capitalize()}${compilation.capitalize()}"
|
||||||
|
|
||||||
|
private fun Project.subplugins(target: String, compilation: String = "main") = this
|
||||||
|
.configurations
|
||||||
|
.getByName(pluginClassPathConfiguration(target, compilation))
|
||||||
|
.allDependencies
|
||||||
|
.map { it.name }
|
||||||
|
|
||||||
|
|
||||||
|
private fun buildProjectWithMPP(code: Project.() -> Unit): ProjectInternal {
|
||||||
|
val project = ProjectBuilder.builder().build()
|
||||||
|
project.plugins.apply("kotlin-multiplatform")
|
||||||
|
project.code()
|
||||||
|
return project as ProjectInternal
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Project.kotlin(code: KotlinMultiplatformExtension.() -> Unit) {
|
||||||
|
val kotlin = project.kotlinExtension as KotlinMultiplatformExtension
|
||||||
|
kotlin.code()
|
||||||
|
}
|
||||||
|
|
||||||
|
private abstract class FakeSubPlugin(
|
||||||
|
val id: String,
|
||||||
|
val idNative: String? = null,
|
||||||
|
val isApplicablePredicate: KotlinCompilation<*>.() -> Boolean
|
||||||
|
) : KotlinCompilerPluginSupportPlugin {
|
||||||
|
override fun isApplicable(kotlinCompilation: KotlinCompilation<*>): Boolean = kotlinCompilation.isApplicablePredicate()
|
||||||
|
|
||||||
|
override fun applyToCompilation(kotlinCompilation: KotlinCompilation<*>): Provider<List<SubpluginOption>> =
|
||||||
|
kotlinCompilation.target.project.provider { emptyList<SubpluginOption>() }
|
||||||
|
|
||||||
|
override fun getCompilerPluginId(): String = id
|
||||||
|
|
||||||
|
override fun getPluginArtifact(): SubpluginArtifact = SubpluginArtifact(
|
||||||
|
"test",
|
||||||
|
id
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun getPluginArtifactForNative(): SubpluginArtifact? = idNative?.let {
|
||||||
|
SubpluginArtifact(
|
||||||
|
"test",
|
||||||
|
it
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-9
@@ -21,7 +21,6 @@ import org.jetbrains.kotlin.gradle.internal.kapt.incremental.KaptIncrementalChan
|
|||||||
import org.jetbrains.kotlin.gradle.internal.tasks.allOutputFiles
|
import org.jetbrains.kotlin.gradle.internal.tasks.allOutputFiles
|
||||||
import org.jetbrains.kotlin.gradle.logging.GradleKotlinLogger
|
import org.jetbrains.kotlin.gradle.logging.GradleKotlinLogger
|
||||||
import org.jetbrains.kotlin.gradle.logging.GradlePrintingMessageCollector
|
import org.jetbrains.kotlin.gradle.logging.GradlePrintingMessageCollector
|
||||||
import org.jetbrains.kotlin.gradle.plugin.PLUGIN_CLASSPATH_CONFIGURATION_NAME
|
|
||||||
import org.jetbrains.kotlin.gradle.tasks.CompilerPluginOptions
|
import org.jetbrains.kotlin.gradle.tasks.CompilerPluginOptions
|
||||||
import org.jetbrains.kotlin.gradle.tasks.GradleCompileTaskProvider
|
import org.jetbrains.kotlin.gradle.tasks.GradleCompileTaskProvider
|
||||||
import org.jetbrains.kotlin.gradle.utils.getValue
|
import org.jetbrains.kotlin.gradle.utils.getValue
|
||||||
@@ -35,14 +34,7 @@ abstract class KaptWithKotlincTask : KaptTask(), CompilerArgumentAwareWithInput<
|
|||||||
|
|
||||||
@get:Classpath
|
@get:Classpath
|
||||||
@get:InputFiles
|
@get:InputFiles
|
||||||
@Suppress("unused")
|
val pluginClasspath: FileCollection get() = kotlinCompileTask.pluginClasspath
|
||||||
internal val kotlinTaskPluginClasspaths
|
|
||||||
get() = kotlinCompileTask.pluginClasspath
|
|
||||||
|
|
||||||
@get:Classpath
|
|
||||||
@get:InputFiles
|
|
||||||
val pluginClasspath: FileCollection
|
|
||||||
get() = project.configurations.getByName(PLUGIN_CLASSPATH_CONFIGURATION_NAME)
|
|
||||||
|
|
||||||
@get:Internal
|
@get:Internal
|
||||||
val taskProvider = GradleCompileTaskProvider(this)
|
val taskProvider = GradleCompileTaskProvider(this)
|
||||||
|
|||||||
+1
-3
@@ -564,9 +564,6 @@ internal abstract class AbstractKotlinPlugin(
|
|||||||
|
|
||||||
// Setup the consuming configurations:
|
// Setup the consuming configurations:
|
||||||
project.dependencies.attributesSchema.attribute(KotlinPlatformType.attribute)
|
project.dependencies.attributesSchema.attribute(KotlinPlatformType.attribute)
|
||||||
kotlinTarget.compilations.all { compilation ->
|
|
||||||
AbstractKotlinTargetConfigurator.defineConfigurationsForCompilation(compilation)
|
|
||||||
}
|
|
||||||
|
|
||||||
project.configurations.getByName("default").apply {
|
project.configurations.getByName("default").apply {
|
||||||
setupAsLocalTargetSpecificConfigurationIfSupported(kotlinTarget)
|
setupAsLocalTargetSpecificConfigurationIfSupported(kotlinTarget)
|
||||||
@@ -595,6 +592,7 @@ internal abstract class AbstractKotlinPlugin(
|
|||||||
buildSourceSetProcessor: (AbstractKotlinCompilation<*>) -> KotlinSourceSetProcessor<*>
|
buildSourceSetProcessor: (AbstractKotlinCompilation<*>) -> KotlinSourceSetProcessor<*>
|
||||||
) {
|
) {
|
||||||
kotlinTarget.compilations.all { compilation ->
|
kotlinTarget.compilations.all { compilation ->
|
||||||
|
AbstractKotlinTargetConfigurator.defineConfigurationsForCompilation(compilation)
|
||||||
buildSourceSetProcessor(compilation).run()
|
buildSourceSetProcessor(compilation).run()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-1
@@ -24,7 +24,6 @@ import org.gradle.api.internal.FeaturePreviews
|
|||||||
import org.gradle.api.logging.Logger
|
import org.gradle.api.logging.Logger
|
||||||
import org.gradle.api.logging.Logging
|
import org.gradle.api.logging.Logging
|
||||||
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
|
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
|
||||||
import org.jetbrains.kotlin.cli.common.CompilerSystemProperties
|
|
||||||
import org.jetbrains.kotlin.gradle.dsl.*
|
import org.jetbrains.kotlin.gradle.dsl.*
|
||||||
import org.jetbrains.kotlin.gradle.logging.kotlinDebug
|
import org.jetbrains.kotlin.gradle.logging.kotlinDebug
|
||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMetadataTarget
|
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMetadataTarget
|
||||||
|
|||||||
+15
@@ -272,6 +272,17 @@ abstract class AbstractKotlinTargetConfigurator<KotlinTargetType : KotlinTarget>
|
|||||||
val target = compilation.target
|
val target = compilation.target
|
||||||
val configurations = target.project.configurations
|
val configurations = target.project.configurations
|
||||||
|
|
||||||
|
val pluginConfiguration = configurations.maybeCreate(compilation.pluginConfigurationName).apply {
|
||||||
|
if (target.platformType == KotlinPlatformType.native) {
|
||||||
|
extendsFrom(configurations.getByName(NATIVE_COMPILER_PLUGIN_CLASSPATH_CONFIGURATION_NAME))
|
||||||
|
} else {
|
||||||
|
extendsFrom(target.project.commonKotlinPluginClasspath)
|
||||||
|
}
|
||||||
|
isVisible = false
|
||||||
|
isCanBeConsumed = false
|
||||||
|
description = "Kotlin compiler plugins for $compilation"
|
||||||
|
}
|
||||||
|
|
||||||
val compileConfiguration = configurations.findByName(compilation.deprecatedCompileConfigurationName)?.apply {
|
val compileConfiguration = configurations.findByName(compilation.deprecatedCompileConfigurationName)?.apply {
|
||||||
isCanBeConsumed = false
|
isCanBeConsumed = false
|
||||||
setupAsLocalTargetSpecificConfigurationIfSupported(target)
|
setupAsLocalTargetSpecificConfigurationIfSupported(target)
|
||||||
@@ -493,3 +504,7 @@ fun Configuration.usesPlatformOf(target: KotlinTarget): Configuration {
|
|||||||
}
|
}
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal val Project.commonKotlinPluginClasspath get() = configurations.getByName(PLUGIN_CLASSPATH_CONFIGURATION_NAME)
|
||||||
|
internal val KotlinCompilation<*>.pluginConfigurationName
|
||||||
|
get() = lowerCamelCaseName(PLUGIN_CLASSPATH_CONFIGURATION_NAME, target.disambiguationClassifier, compilationName)
|
||||||
+5
-6
@@ -66,12 +66,11 @@ class SubpluginEnvironment(
|
|||||||
val pluginId = subplugin.getCompilerPluginId()
|
val pluginId = subplugin.getCompilerPluginId()
|
||||||
project.logger.kotlinDebug { "Loading subplugin $pluginId" }
|
project.logger.kotlinDebug { "Loading subplugin $pluginId" }
|
||||||
|
|
||||||
subplugin.getPluginArtifact().let { artifact ->
|
val nativeArtifact = subplugin.getPluginArtifactForNative()
|
||||||
project.addMavenDependency(PLUGIN_CLASSPATH_CONFIGURATION_NAME, artifact)
|
if (nativeArtifact != null && kotlinCompilation.platformType == KotlinPlatformType.native) {
|
||||||
}
|
project.addMavenDependency(kotlinCompilation.pluginConfigurationName, nativeArtifact)
|
||||||
|
} else {
|
||||||
subplugin.getPluginArtifactForNative()?.let { artifact ->
|
project.addMavenDependency(kotlinCompilation.pluginConfigurationName, subplugin.getPluginArtifact())
|
||||||
project.addMavenDependency(NATIVE_COMPILER_PLUGIN_CLASSPATH_CONFIGURATION_NAME, artifact)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val subpluginOptionsProvider = subplugin.applyToCompilation(kotlinCompilation)
|
val subpluginOptionsProvider = subplugin.applyToCompilation(kotlinCompilation)
|
||||||
|
|||||||
+3
-1
@@ -65,7 +65,9 @@ open class KotlinNativeTargetConfigurator<T : KotlinNativeTarget>(
|
|||||||
.addSubpluginOptions(project, compilation)
|
.addSubpluginOptions(project, compilation)
|
||||||
|
|
||||||
compilation.compileKotlinTaskProvider.configure {
|
compilation.compileKotlinTaskProvider.configure {
|
||||||
it.compilerPluginClasspath = project.configurations.getByName(NATIVE_COMPILER_PLUGIN_CLASSPATH_CONFIGURATION_NAME)
|
it.compilerPluginClasspath = project
|
||||||
|
.configurations
|
||||||
|
.getByName(compilation.pluginConfigurationName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -38,10 +38,8 @@ import org.jetbrains.kotlin.gradle.internal.*
|
|||||||
import org.jetbrains.kotlin.gradle.internal.tasks.TaskWithLocalState
|
import org.jetbrains.kotlin.gradle.internal.tasks.TaskWithLocalState
|
||||||
import org.jetbrains.kotlin.gradle.internal.tasks.allOutputFiles
|
import org.jetbrains.kotlin.gradle.internal.tasks.allOutputFiles
|
||||||
import org.jetbrains.kotlin.gradle.logging.*
|
import org.jetbrains.kotlin.gradle.logging.*
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.*
|
||||||
import org.jetbrains.kotlin.gradle.plugin.COMPILER_CLASSPATH_CONFIGURATION_NAME
|
import org.jetbrains.kotlin.gradle.plugin.COMPILER_CLASSPATH_CONFIGURATION_NAME
|
||||||
import org.jetbrains.kotlin.gradle.plugin.KotlinMultiplatformPluginWrapper
|
|
||||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformPluginBase
|
|
||||||
import org.jetbrains.kotlin.gradle.plugin.PLUGIN_CLASSPATH_CONFIGURATION_NAME
|
|
||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.AbstractKotlinCompilation
|
import org.jetbrains.kotlin.gradle.plugin.mpp.AbstractKotlinCompilation
|
||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.associateWithTransitiveClosure
|
import org.jetbrains.kotlin.gradle.plugin.mpp.associateWithTransitiveClosure
|
||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.ownModuleName
|
import org.jetbrains.kotlin.gradle.plugin.mpp.ownModuleName
|
||||||
@@ -219,7 +217,9 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractKo
|
|||||||
|
|
||||||
@get:InputFiles
|
@get:InputFiles
|
||||||
@get:Classpath
|
@get:Classpath
|
||||||
open val pluginClasspath: FileCollection = project.configurations.getByName(PLUGIN_CLASSPATH_CONFIGURATION_NAME)
|
open val pluginClasspath: FileCollection by project.provider {
|
||||||
|
project.configurations.getByName(taskData.compilation.pluginConfigurationName)
|
||||||
|
}
|
||||||
|
|
||||||
@get:Internal
|
@get:Internal
|
||||||
internal val pluginOptions = CompilerPluginOptions()
|
internal val pluginOptions = CompilerPluginOptions()
|
||||||
|
|||||||
Reference in New Issue
Block a user