Add Kotlin SourceDirectorySet as extension

KotlinSourceSet now should be accessed via Kotlin project extension.
Conventions are still available, but not used inside plugins.

^KT-47047 In Progress
This commit is contained in:
Yahor Berdnikau
2022-06-13 14:22:25 +02:00
committed by Space
parent 9e8524d21a
commit ec545f6273
15 changed files with 118 additions and 84 deletions
@@ -142,9 +142,6 @@ sourceSets {
srcDirs("prebuilt/nativeInteropStubs/kotlin")
}
kotlin{
target {
}
}
}
}
@@ -246,4 +246,13 @@ class SimpleKotlinGradleIT : KGPBaseTest() {
build("validateExternalPlugins")
}
}
@DisplayName("Accessing Kotlin SourceSet in KotlinDSL")
@GradleTestVersions(maxVersion = TestVersions.Gradle.G_7_1)
@GradleTest
internal fun kotlinDslSourceSets(gradleVersion: GradleVersion) {
project("sourceSetsKotlinDsl", gradleVersion) {
build("assemble")
}
}
}
@@ -0,0 +1,16 @@
plugins {
id("org.jetbrains.kotlin.jvm")
}
repositories {
mavenLocal()
mavenCentral()
}
sourceSets {
main {
kotlin {
setSrcDirs(listOf("src/main/kotlin", "src/shared/kotlin"))
}
}
}
@@ -0,0 +1,12 @@
/*
* Copyright 2010-2022 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 sourceSetsKotlinDsl
import sourceSetsKotlinDsl.shared.Greeter
fun main() {
Greeter("World").printGreet()
}
@@ -0,0 +1,12 @@
/*
* Copyright 2010-2022 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 sourceSetsKotlinDsl.shared
class Greeter(private val name: String) {
fun printGreet() {
println("Hello, $name!")
}
}
@@ -8,7 +8,7 @@ package org.jetbrains.kotlin.gradle.model.builder
import org.gradle.api.Project
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.ProjectDependency
import org.gradle.api.plugins.JavaPluginConvention
import org.gradle.api.file.SourceDirectorySet
import org.gradle.tooling.provider.model.ToolingModelBuilder
import org.jetbrains.kotlin.gradle.model.CompilerArguments
import org.jetbrains.kotlin.gradle.model.KotlinProject
@@ -17,7 +17,7 @@ import org.jetbrains.kotlin.gradle.model.impl.CompilerArgumentsImpl
import org.jetbrains.kotlin.gradle.model.impl.KotlinProjectImpl
import org.jetbrains.kotlin.gradle.model.impl.SourceSetImpl
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.getConvention
import org.jetbrains.kotlin.gradle.plugin.internal.JavaSourceSetsAccessor
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
@@ -76,16 +76,19 @@ class KotlinModelBuilder(private val kotlinPluginVersion: String, private val an
private fun Project.pathOrName() = if (path == ":") name else path
private fun AbstractKotlinCompile<*>.createSourceSet(project: Project, projectType: KotlinProject.ProjectType): SourceSet? {
val javaSourceSet =
project.convention.findPlugin(JavaPluginConvention::class.java)?.sourceSets?.find { it.name == sourceSetName.get() }
val kotlinSourceSet =
javaSourceSet?.getConvention(if (projectType == KotlinProject.ProjectType.PLATFORM_JS) KOTLIN_JS_DSL_NAME else KOTLIN_DSL_NAME) as? KotlinSourceSet
val javaSourceSet = project.gradle
.variantImplementationFactory<JavaSourceSetsAccessor.JavaSourceSetsAccessorVariantFactory>()
.getInstance(project)
.sourceSetsIfAvailable
?.find { it.name == sourceSetName.get() }
@Suppress("DEPRECATION") val kotlinSourceSet: SourceDirectorySet? = javaSourceSet
?.getExtension(if (projectType == KotlinProject.ProjectType.PLATFORM_JS) KOTLIN_JS_DSL_NAME else KOTLIN_DSL_NAME)
return if (kotlinSourceSet != null) {
SourceSetImpl(
sourceSetName.get(),
if (sourceSetName.get().contains("test", true)) SourceSet.SourceSetType.TEST else SourceSet.SourceSetType.PRODUCTION,
friendSourceSets.get(),
kotlinSourceSet.kotlin.srcDirs,
kotlinSourceSet.srcDirs,
javaSourceSet.resources.srcDirs,
destinationDirectory.get().asFile,
javaSourceSet.output.resourcesDir!!,
@@ -194,12 +194,8 @@ open class KotlinPlatformImplementationPluginBase(platformName: String) : Kotlin
@Deprecated("Retained for older Kotlin/Native MPP plugin binary compatibility")
protected val SourceSet.kotlin: SourceDirectorySet?
get() {
// Access through reflection, because another project's KotlinSourceSet might be loaded
// by a different class loader:
val convention = (getConvention("kotlin") ?: getConvention("kotlin2js")) ?: return null
val kotlinSourceSetIface = convention.javaClass.interfaces.find { it.name == KotlinSourceSet::class.qualifiedName }
val getKotlin = kotlinSourceSetIface?.methods?.find { it.name == "getKotlin" } ?: return null
return getKotlin(convention) as? SourceDirectorySet
@Suppress("DEPRECATION")
return getExtension(KOTLIN_DSL_NAME) ?: getExtension(KOTLIN_JS_DSL_NAME)
}
}
@@ -35,6 +35,7 @@ import org.jetbrains.kotlin.gradle.internal.checkAndroidAnnotationProcessorDepen
import org.jetbrains.kotlin.gradle.internal.customizeKotlinDependencies
import org.jetbrains.kotlin.gradle.logging.kotlinDebug
import org.jetbrains.kotlin.gradle.model.builder.KotlinModelBuilder
import org.jetbrains.kotlin.gradle.plugin.AbstractAndroidProjectHandler.Companion.kotlinSourceSetNameForAndroidSourceSet
import org.jetbrains.kotlin.gradle.plugin.internal.JavaSourceSetsAccessor
import org.jetbrains.kotlin.gradle.plugin.internal.MavenPluginConfigurator
import org.jetbrains.kotlin.gradle.plugin.mpp.*
@@ -60,6 +61,7 @@ internal const val COMPILER_CLASSPATH_CONFIGURATION_NAME = "kotlinCompilerClassp
internal const val KLIB_COMMONIZER_CLASSPATH_CONFIGURATION_NAME = "kotlinKlibCommonizerClasspath"
val KOTLIN_DSL_NAME = "kotlin"
@Deprecated("Should be removed with 'platform.js' plugin removal")
val KOTLIN_JS_DSL_NAME = "kotlin2js"
val KOTLIN_OPTIONS_DSL_NAME = "kotlinOptions"
@@ -491,7 +493,7 @@ internal abstract class AbstractKotlinPlugin(
.getInstance(project)
.sourceSets
val kotlinSourceSetDslName = when (kotlinTarget.platformType) {
@Suppress("DEPRECATION") val kotlinSourceSetDslName = when (kotlinTarget.platformType) {
KotlinPlatformType.js -> KOTLIN_JS_DSL_NAME
else -> KOTLIN_DSL_NAME
}
@@ -508,9 +510,13 @@ internal abstract class AbstractKotlinPlugin(
val kotlinSourceSet = project.kotlinExtension.sourceSets.maybeCreate(kotlinCompilation.name)
kotlinSourceSet.kotlin.source(javaSourceSet.java)
kotlinCompilation.source(kotlinSourceSet)
@Suppress("DEPRECATION")
javaSourceSet.addConvention(kotlinSourceSetDslName, kotlinSourceSet)
javaSourceSet.addExtension(kotlinSourceSetDslName, kotlinSourceSet.kotlin)
} else {
@Suppress("DEPRECATION")
javaSourceSet.addConvention(kotlinSourceSetDslName, kotlinCompilation.defaultSourceSet)
javaSourceSet.addExtension(kotlinSourceSetDslName, kotlinCompilation.defaultSourceSet.kotlin)
}
}
@@ -868,45 +874,6 @@ abstract class AbstractAndroidProjectHandler(private val kotlinConfigurationTool
)
}
}
// Then also add the Kotlin compilation dependencies (which include the dependencies from all source sets that
// take part in the compilation) to Android source sets that are only included into a single variant corresponding
// to that compilation. This is needed in order for the dependencies to get propagated to
// the test variants; see KT-29343;
// Trivial mapping of Android variants to Android source set names is impossible here,
// because some variants have their dedicated source sets with mismatching names,
// because some variants have their dedicated source sets with mismatching names,
// e.g. variant 'fooBarDebugAndroidTest' <-> source set 'androidTestFooBarDebug'
// In single-platform projects, the Kotlin compilations already reference the Android plugin's configurations by the names,
// so there are no such separate things as the configurations of the compilations, and there's no need to setup the
// extendsFrom relationship.
if (kotlinAndroidTarget.disambiguationClassifier != null) {
val sourceSetToVariants = mutableMapOf<AndroidSourceSet, MutableList<BaseVariant>>().apply {
forEachVariant(project) { variant ->
for (sourceSet in variant.sourceSets) {
val androidSourceSet = sourceSet as? AndroidSourceSet ?: continue
getOrPut(androidSourceSet) { mutableListOf() }.add(variant)
}
}
}
for ((androidSourceSet, variants) in sourceSetToVariants) {
val variant = variants.singleOrNull()
?: continue // skip source sets that are included in multiple Android variants
val compilation = kotlinAndroidTarget.compilations.getByName(getVariantName(variant))
addDependenciesToAndroidSourceSet(
androidSourceSet,
compilation.apiConfigurationName,
compilation.implementationConfigurationName,
compilation.compileOnlyConfigurationName,
compilation.runtimeOnlyConfigurationName
)
}
}
}
private fun addAndroidUnitTestTasksAsDependenciesToAllTest(project: Project) {
@@ -956,7 +923,12 @@ abstract class AbstractAndroidProjectHandler(private val kotlinConfigurationTool
// Register the source only after the task is created, because the task is required for that:
compilation.source(defaultSourceSet)
compilation.androidVariant.forEachKotlinSourceSet { kotlinSourceSet -> compilation.source(kotlinSourceSet) }
compilation.androidVariant.forEachKotlinSourceSet(
compilation.target as KotlinAndroidTarget,
project.kotlinExtension
) { kotlinSourceSet ->
compilation.source(kotlinSourceSet)
}
}
private fun postprocessVariant(
@@ -985,9 +957,22 @@ abstract class AbstractAndroidProjectHandler(private val kotlinConfigurationTool
}
}
internal inline fun BaseVariant.forEachKotlinSourceSet(action: (KotlinSourceSet) -> Unit) {
internal inline fun BaseVariant.forEachKotlinSourceSet(
kotlinAndroidTarget: KotlinAndroidTarget,
kotlinExtension: KotlinProjectExtension,
action: (KotlinSourceSet) -> Unit
) {
sourceSets
.mapNotNull { provider -> provider.getConvention(KOTLIN_DSL_NAME) as? KotlinSourceSet }
.mapNotNull { provider ->
val kotlinSourceSetName = kotlinSourceSetNameForAndroidSourceSet(kotlinAndroidTarget, provider.name)
kotlinExtension.sourceSets.findByName(kotlinSourceSetName)
}
.forEach(action)
}
internal inline fun BaseVariant.forEachKotlinSourceDirectorySet(action: (SourceDirectorySet) -> Unit) {
sourceSets
.mapNotNull { it.getExtension<SourceDirectorySet>(KOTLIN_DSL_NAME) }
.forEach(action)
}
@@ -94,8 +94,8 @@ internal fun addCompilationSourcesToExternalCompileTask(
task: TaskProvider<out AbstractKotlinCompileTool<*>>
) {
if (compilation is KotlinJvmAndroidCompilation) {
compilation.androidVariant.forEachKotlinSourceSet { sourceSet ->
task.configure { it.setSource(sourceSet.kotlin) }
compilation.androidVariant.forEachKotlinSourceDirectorySet { sourceSet ->
task.configure { it.setSource(sourceSet) }
}
compilation.androidVariant.forEachJavaSourceDir { sources ->
task.configure { it.setSource(sources.dir) }
@@ -14,11 +14,14 @@
* limitations under the License.
*/
@file:Suppress("DEPRECATION")
package org.jetbrains.kotlin.gradle.plugin
import org.gradle.api.internal.HasConvention
import org.gradle.api.plugins.ExtensionAware
@Deprecated("Conventions are deprecated in Gradle")
internal inline fun <reified T : Any> Any.addConvention(name: String, plugin: T) {
(this as HasConvention).convention.plugins[name] = plugin
}
@@ -26,5 +29,5 @@ internal inline fun <reified T : Any> Any.addConvention(name: String, plugin: T)
internal inline fun <reified T : Any> Any.addExtension(name: String, extension: T) =
(this as ExtensionAware).extensions.add(T::class.java, name, extension)
internal fun Any.getConvention(name: String): Any? =
(this as HasConvention).convention.plugins[name]
internal inline fun <reified T : Any> Any.getExtension(name: String): T? =
(this as ExtensionAware).extensions.getByName(name) as T?
@@ -7,15 +7,14 @@ package org.jetbrains.kotlin.gradle.plugin.mpp
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.api.AndroidSourceSet
import org.gradle.api.Project
import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.internal.plugins.DslObject
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.AbstractAndroidProjectHandler.Companion.kotlinSourceSetNameForAndroidSourceSet
import org.jetbrains.kotlin.gradle.plugin.addConvention
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinSourceSetFactory
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinSourceSetFactory.Companion.defaultSourceFolder
import java.io.File
internal fun syncKotlinAndAndroidSourceSets(target: KotlinAndroidTarget) {
val project = target.project
@@ -24,7 +23,7 @@ internal fun syncKotlinAndAndroidSourceSets(target: KotlinAndroidTarget) {
android.sourceSets.all { androidSourceSet ->
val kotlinSourceSetName = kotlinSourceSetNameForAndroidSourceSet(target, androidSourceSet.name)
val kotlinSourceSet = project.kotlinExtension.sourceSets.maybeCreate(kotlinSourceSetName)
androidSourceSet.kotlinSourceSet = kotlinSourceSet
androidSourceSet.addKotlinSources(kotlinSourceSet)
createDefaultDependsOnEdges(target, kotlinSourceSet, androidSourceSet)
syncKotlinAndAndroidSourceDirs(target, kotlinSourceSet, androidSourceSet)
@@ -36,14 +35,13 @@ internal fun syncKotlinAndAndroidSourceSets(target: KotlinAndroidTarget) {
}
}
internal var AndroidSourceSet.kotlinSourceSet: KotlinSourceSet
get() = checkNotNull(kotlinSourceSetOrNull) { "Missing kotlinSourceSet for Android source set $name" }
private set(value) {
addConvention(KOTLIN_DSL_NAME, value)
}
internal val AndroidSourceSet.kotlinSourceSet: SourceDirectorySet
get() = checkNotNull(getExtension(KOTLIN_DSL_NAME)) { "Missing kotlinSourceSet for Android source set $name" }
internal val AndroidSourceSet.kotlinSourceSetOrNull: KotlinSourceSet?
get() = getConvention(KOTLIN_DSL_NAME) as? KotlinSourceSet
internal fun AndroidSourceSet.addKotlinSources(kotlinSourceSet: KotlinSourceSet) {
@Suppress("DEPRECATION") addConvention(KOTLIN_DSL_NAME, kotlinSourceSet)
DslObject(this).addExtension(KOTLIN_DSL_NAME, kotlinSourceSet.kotlin)
}
private fun createDefaultDependsOnEdges(
target: KotlinAndroidTarget,
@@ -107,7 +107,7 @@ class SyncKotlinAndAndroidSourceSetsTest {
fun assertSourceSetsExist(androidName: String, kotlinName: String) {
val androidSourceSet = assertNotNull(android.sourceSets.findByName(androidName), "Expected Android source set '$androidName'")
val kotlinSourceSet = assertNotNull(kotlin.sourceSets.findByName(kotlinName), "Expected Kotlin source set '$kotlinName'")
assertSame(kotlinSourceSet, androidSourceSet.kotlinSourceSet)
assertSame(kotlinSourceSet.kotlin, androidSourceSet.kotlinSourceSet)
}
assertSourceSetsExist("freeBetaDebug", "androidFreeBetaDebug")
@@ -182,13 +182,13 @@ class SyncKotlinAndAndroidSourceSetsTest {
kotlin.android()
val main = android.sourceSets.getByName("main")
assertSame(kotlin.sourceSets.getByName("androidMain"), main.kotlinSourceSet)
assertSame(kotlin.sourceSets.getByName("androidMain").kotlin, main.kotlinSourceSet)
val test = android.sourceSets.getByName("test")
assertSame(kotlin.sourceSets.getByName("androidTest"), test.kotlinSourceSet)
assertSame(kotlin.sourceSets.getByName("androidTest").kotlin, test.kotlinSourceSet)
val androidTest = android.sourceSets.getByName("androidTest")
assertSame(kotlin.sourceSets.getByName("androidAndroidTest"), androidTest.kotlinSourceSet)
assertSame(kotlin.sourceSets.getByName("androidAndroidTest").kotlin, androidTest.kotlinSourceSet)
}
}
@@ -9,6 +9,7 @@ import com.android.build.gradle.BaseExtension
import org.gradle.api.Named
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.model.ObjectFactory
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
import org.jetbrains.kotlin.gradle.plugin.internal.JavaSourceSetsAccessor
@@ -120,9 +121,9 @@ open class KotlinPlatformAndroidPlugin : KotlinPlatformImplementationPluginBase(
override fun addCommonSourceSetToPlatformSourceSet(commonSourceSet: Named, platformProject: Project) {
val androidExtension = platformProject.extensions.getByName("android") as BaseExtension
val androidSourceSet = androidExtension.sourceSets.findByName(commonSourceSet.name) ?: return
val kotlinSourceSet = androidSourceSet.getConvention(KOTLIN_DSL_NAME) as? KotlinSourceSet
val kotlinSourceSet = androidSourceSet.getExtension<SourceDirectorySet>(KOTLIN_DSL_NAME)
?: return
kotlinSourceSet.kotlin.source(getKotlinSourceDirectorySetSafe(commonSourceSet)!!)
kotlinSourceSet.source(getKotlinSourceDirectorySetSafe(commonSourceSet)!!)
}
}
@@ -9,6 +9,7 @@ import com.android.build.gradle.BaseExtension
import org.gradle.api.Named
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.model.ObjectFactory
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
import javax.inject.Inject
@@ -123,9 +124,9 @@ open class KotlinPlatformAndroidPlugin : KotlinPlatformImplementationPluginBase(
override fun addCommonSourceSetToPlatformSourceSet(commonSourceSet: Named, platformProject: Project) {
val androidExtension = platformProject.extensions.getByName("android") as BaseExtension
val androidSourceSet = androidExtension.sourceSets.findByName(commonSourceSet.name) ?: return
val kotlinSourceSet = androidSourceSet.getConvention(KOTLIN_DSL_NAME) as? KotlinSourceSet
val kotlinSourceSet = androidSourceSet.getExtension<SourceDirectorySet>(KOTLIN_DSL_NAME)
?: return
kotlinSourceSet.kotlin.source(getKotlinSourceDirectorySetSafe(commonSourceSet)!!)
kotlinSourceSet.source(getKotlinSourceDirectorySetSafe(commonSourceSet)!!)
}
}
@@ -9,6 +9,7 @@ import com.android.build.gradle.BaseExtension
import org.gradle.api.Named
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.model.ObjectFactory
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
import org.jetbrains.kotlin.gradle.plugin.internal.JavaSourceSetsAccessor
@@ -127,9 +128,9 @@ open class KotlinPlatformAndroidPlugin : KotlinPlatformImplementationPluginBase(
override fun addCommonSourceSetToPlatformSourceSet(commonSourceSet: Named, platformProject: Project) {
val androidExtension = platformProject.extensions.getByName("android") as BaseExtension
val androidSourceSet = androidExtension.sourceSets.findByName(commonSourceSet.name) ?: return
val kotlinSourceSet = androidSourceSet.getConvention(KOTLIN_DSL_NAME) as? KotlinSourceSet
val kotlinSourceSet = androidSourceSet.getExtension<SourceDirectorySet>(KOTLIN_DSL_NAME)
?: return
kotlinSourceSet.kotlin.source(getKotlinSourceDirectorySetSafe(commonSourceSet)!!)
kotlinSourceSet.source(getKotlinSourceDirectorySetSafe(commonSourceSet)!!)
}
}