Split dependency management code into 3 files

^KT-41642 Fixed
This commit is contained in:
Yahor Berdnikau
2022-07-30 23:06:29 +02:00
committed by Space
parent 632009345d
commit 409a85f552
5 changed files with 415 additions and 371 deletions
@@ -94,7 +94,7 @@ class ConfigurationCacheIT : AbstractConfigurationCacheIT() {
build("build", buildOptions = buildOptions) {
// Reduce the problem numbers when a Task become compatible with GCC.
// When all tasks support GCC, replace these assertions with `testConfigurationCacheOf`
assertOutputContains("15 problems were found storing the configuration cache, 5 of which seem unique.")
assertOutputContains("16 problems were found storing the configuration cache, 6 of which seem unique.")
configCacheIncompatibleTaskTypes.forEach { taskType ->
assertOutputContains(
"""Task `\S+` of type `[\w.]+$taskType`: .+(at execution time is unsupported)|(not supported with the configuration cache)"""
@@ -201,7 +201,6 @@ class KotlinSpecificDependenciesIT : KGPBaseTest() {
buildGradle.appendText(
"""
kotlin.target.compilations["main"].kotlinOptions.jvmTarget = "1.8"
dependencies { implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") }
""".trimIndent()
)
@@ -669,7 +668,7 @@ class KotlinSpecificDependenciesIT : KGPBaseTest() {
""".trimIndent()
)
build("${subproject?.prependIndent(":").orEmpty()}:$printingTaskName", forceOutput = true) {
build("${subproject?.prependIndent(":").orEmpty()}:$printingTaskName") {
val itemsLine = output.lines().single { "###$printingTaskName" in it }.substringAfter(printingTaskName)
val items = itemsLine.removeSurrounding("[", "]").split(", ").toSet()
checkAnyItemsContains.forEach { pattern ->
@@ -5,40 +5,14 @@
package org.jetbrains.kotlin.gradle.internal
import com.android.build.gradle.api.TestVariant
import com.android.build.gradle.api.UnitTestVariant
import org.gradle.api.NamedDomainObjectSet
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.*
import org.gradle.api.artifacts.dsl.DependencyHandler
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.TaskContainer
import org.gradle.api.tasks.TaskProvider
import org.gradle.api.tasks.testing.Test
import org.gradle.api.tasks.testing.junit.JUnitOptions
import org.gradle.api.tasks.testing.junitplatform.JUnitPlatformOptions
import org.gradle.api.tasks.testing.testng.TestNGOptions
import org.jetbrains.kotlin.gradle.dsl.*
import org.jetbrains.kotlin.gradle.execution.KotlinAggregateExecutionSource
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
import org.jetbrains.kotlin.gradle.plugin.mpp.*
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.GradleKpmFragment
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.GradleKpmModule
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.hasKpmModel
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.kpmModules
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope
import org.jetbrains.kotlin.gradle.plugin.sources.android.AndroidBaseSourceSetName
import org.jetbrains.kotlin.gradle.plugin.sources.android.AndroidVariantType
import org.jetbrains.kotlin.gradle.plugin.sources.android.androidSourceSetInfoOrNull
import org.jetbrains.kotlin.gradle.plugin.sources.dependsOnClosure
import org.jetbrains.kotlin.gradle.plugin.sources.sourceSetDependencyConfigurationByScope
import org.jetbrains.kotlin.gradle.targets.js.npm.SemVer
import org.jetbrains.kotlin.gradle.targets.jvm.JvmCompilationsTestRunSource
import org.jetbrains.kotlin.gradle.tasks.locateTask
import org.jetbrains.kotlin.gradle.testing.KotlinTaskTestRun
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import org.jetbrains.kotlin.gradle.utils.providerWithLazyConvention
import org.jetbrains.kotlin.gradle.utils.withType
@@ -122,348 +96,7 @@ private fun KotlinTarget.excludeStdlibAndKotlinTestCommonFromPlatformCompilation
}
}
//region stdlib
private fun Project.configureStdlibDefaultDependency(
topLevelExtension: KotlinTopLevelExtension,
coreLibrariesVersion: Provider<String>
) {
when {
project.hasKpmModel -> addStdlibToKpmProject(project, coreLibrariesVersion)
topLevelExtension is KotlinJsProjectExtension -> topLevelExtension.registerTargetObserver { target ->
target?.addStdlibDependency(configurations, dependencies, coreLibrariesVersion)
}
topLevelExtension is KotlinSingleTargetExtension<*> -> topLevelExtension
.target
.addStdlibDependency(configurations, dependencies, coreLibrariesVersion)
topLevelExtension is KotlinMultiplatformExtension -> topLevelExtension
.targets
.configureEach { target ->
target.addStdlibDependency(configurations, dependencies, coreLibrariesVersion)
}
}
}
private fun addStdlibToKpmProject(
project: Project,
coreLibrariesVersion: Provider<String>
) {
project.kpmModules.named(GradleKpmModule.MAIN_MODULE_NAME) { main ->
main.fragments.named(GradleKpmFragment.COMMON_FRAGMENT_NAME) { common ->
common.dependencies {
api(project.dependencies.kotlinDependency("kotlin-stdlib-common", coreLibrariesVersion.get()))
}
}
main.variants.configureEach { variant ->
val dependencyHandler = project.dependencies
val stdlibModule = when (variant.platformType) {
KotlinPlatformType.common -> error("variants are not expected to be common")
KotlinPlatformType.jvm -> chooseStdlibJvmDependency(coreLibrariesVersion)
KotlinPlatformType.js -> "kotlin-stdlib-js"
KotlinPlatformType.wasm -> "kotlin-stdlib-wasm"
KotlinPlatformType.androidJvm -> null // TODO: expect support on the AGP side?
KotlinPlatformType.native -> null
}
if (stdlibModule != null) {
variant.dependencies {
api(dependencyHandler.kotlinDependency(stdlibModule, coreLibrariesVersion.get()))
}
}
}
}
}
private fun KotlinTarget.addStdlibDependency(
configurations: ConfigurationContainer,
dependencies: DependencyHandler,
coreLibrariesVersion: Provider<String>
) {
compilations.configureEach { compilation ->
compilation.allKotlinSourceSets.forEach { kotlinSourceSet ->
val scope = if (compilation.isTest() ||
(this is KotlinAndroidTarget &&
kotlinSourceSet.isRelatedToAndroidTestSourceSet()
)
) {
KotlinDependencyScope.IMPLEMENTATION_SCOPE
} else {
KotlinDependencyScope.API_SCOPE
}
val scopeConfiguration = configurations
.sourceSetDependencyConfigurationByScope(kotlinSourceSet, scope)
scopeConfiguration.withDependencies { dependencySet ->
// Check if stdlib is directly added to SourceSet
if (isStdlibAddedByUser(configurations, stdlibModules, kotlinSourceSet)) return@withDependencies
val stdlibModule = compilation
.platformType
.stdlibPlatformType(coreLibrariesVersion, this, kotlinSourceSet)
?: return@withDependencies
// Check if stdlib module is added to SourceSets hierarchy
if (
isStdlibAddedByUser(
configurations,
setOf(stdlibModule),
*kotlinSourceSet.dependsOnClosure.toTypedArray()
)
) return@withDependencies
dependencySet.addLater(
coreLibrariesVersion.map {
dependencies.kotlinDependency(stdlibModule, it)
}
)
}
}
}
}
private fun isStdlibAddedByUser(
configurations: ConfigurationContainer,
stdlibModules: Set<String>,
vararg sourceSets: KotlinSourceSet
): Boolean {
return sourceSets
.asSequence()
.flatMap { sourceSet ->
KotlinDependencyScope.values().map { scope ->
configurations.sourceSetDependencyConfigurationByScope(sourceSet, scope)
}.asSequence()
}
.flatMap { it.allNonProjectDependencies().asSequence() }
.any { dependency ->
dependency.group == KOTLIN_MODULE_GROUP && dependency.name in stdlibModules
}
}
private fun KotlinPlatformType.stdlibPlatformType(
coreLibrariesVersion: Provider<String>,
kotlinTarget: KotlinTarget,
kotlinSourceSet: KotlinSourceSet
): String? = when (this) {
KotlinPlatformType.jvm -> chooseStdlibJvmDependency(coreLibrariesVersion)
KotlinPlatformType.androidJvm -> {
if (kotlinTarget is KotlinAndroidTarget &&
kotlinSourceSet.androidSourceSetInfoOrNull?.androidSourceSetName == AndroidBaseSourceSetName.Main.name
) {
chooseStdlibJvmDependency(coreLibrariesVersion)
} else {
null
}
}
KotlinPlatformType.js -> "kotlin-stdlib-js"
KotlinPlatformType.wasm -> "kotlin-stdlib-wasm"
KotlinPlatformType.native -> null
KotlinPlatformType.common -> // there's no platform compilation that the source set is default for
"kotlin-stdlib-common"
}
private val kotlin180Version = SemVer(1.toBigInteger(), 8.toBigInteger(), 0.toBigInteger())
private fun chooseStdlibJvmDependency(
coreLibrariesVersion: Provider<String>
): String {
// Current 'SemVer.satisfies' release always returns `false` for any "-SNAPSHOT" version.
return if (SemVer.from(coreLibrariesVersion.get()) < kotlin180Version) {
"kotlin-stdlib-jdk8"
} else {
"kotlin-stdlib"
}
}
private val androidTestVariants = setOf(AndroidVariantType.UnitTest, AndroidVariantType.InstrumentedTest)
private fun KotlinSourceSet.isRelatedToAndroidTestSourceSet(): Boolean {
val androidVariant = androidSourceSetInfoOrNull?.androidVariantType ?: return false
return androidVariant in androidTestVariants
}
private val stdlibModules = setOf("kotlin-stdlib-common", "kotlin-stdlib", "kotlin-stdlib-jdk7", "kotlin-stdlib-jdk8", "kotlin-stdlib-js")
//endregion
//region kotlin-test
private val Dependency.isKotlinTestRootDependency: Boolean
get() = group == KOTLIN_MODULE_GROUP && name == KOTLIN_TEST_ROOT_MODULE_NAME
private val kotlin150Version = SemVer(1.toBigInteger(), 5.toBigInteger(), 0.toBigInteger())
private fun isAtLeast1_5(version: String) = SemVer.from(version) >= kotlin150Version
private val jvmPlatforms = setOf(KotlinPlatformType.jvm, KotlinPlatformType.androidJvm)
private fun Project.configureKotlinTestDependency(
topLevelExtension: KotlinTopLevelExtension,
coreLibrariesVersion: Provider<String>,
) {
when (topLevelExtension) {
is KotlinJsProjectExtension -> topLevelExtension.registerTargetObserver { target ->
target?.configureKotlinTestDependency(
configurations,
coreLibrariesVersion,
objects,
dependencies,
tasks
)
}
is KotlinSingleTargetExtension<*> -> topLevelExtension.target.configureKotlinTestDependency(
configurations,
coreLibrariesVersion,
dependencies,
tasks
)
is KotlinMultiplatformExtension -> topLevelExtension.targets.configureEach { target ->
target.configureKotlinTestDependency(
configurations,
coreLibrariesVersion,
dependencies,
tasks
)
}
}
}
private fun KotlinTarget.configureKotlinTestDependency(
configurations: ConfigurationContainer,
coreLibrariesVersion: Provider<String>,
dependencyHandler: DependencyHandler,
tasks: TaskContainer
) {
compilations.configureEach { compilation ->
val platformType = compilation.platformType
if (platformType in jvmPlatforms) {
// Checking dependencies which were added via dependsOn(KotlinSourceSet) call
// Compilation has own configurations for these that are different from KotlinSourceSet configurations
KotlinDependencyScope.values()
.map { configurations.sourceSetDependencyConfigurationByScope(compilation, it) }
.forEach {
it.maybeAddTestDependencyCapability(
compilation,
coreLibrariesVersion,
dependencyHandler,
tasks
)
}
compilation.kotlinSourceSets.forEach { sourceSet ->
KotlinDependencyScope.values()
.map { configurations.sourceSetDependencyConfigurationByScope(sourceSet, it) }
.forEach {
it.maybeAddTestDependencyCapability(
compilation,
coreLibrariesVersion,
dependencyHandler,
tasks
)
}
}
}
}
}
private fun Configuration.maybeAddTestDependencyCapability(
compilation: KotlinCompilation<*>,
coreLibrariesVersion: Provider<String>,
dependencyHandler: DependencyHandler,
tasks: TaskContainer
) {
withDependencies { dependencies ->
val testRootDependency = allNonProjectDependencies()
.singleOrNull { it.isKotlinTestRootDependency }
if (testRootDependency != null) {
val depVersion = testRootDependency.version ?: coreLibrariesVersion.get()
if (!isAtLeast1_5(depVersion)) return@withDependencies
val testCapability = compilation.kotlinTestCapabilityForJvmSourceSet(tasks)
if (testCapability != null) {
dependencies.addLater(
testCapability.map { capability ->
dependencyHandler
.kotlinDependency(KOTLIN_TEST_ROOT_MODULE_NAME, depVersion)
.apply {
(this as ExternalDependency).capabilities {
it.requireCapability(capability)
}
}
}
)
}
}
}
}
private fun KotlinCompilation<*>.kotlinTestCapabilityForJvmSourceSet(
tasks: TaskContainer
): Provider<String>? {
val compilationTarget = target
val testTaskList: List<TaskProvider<out Task>> = when {
compilationTarget is KotlinTargetWithTests<*, *> -> compilationTarget
.findTestRunsByCompilation(this)
.matching { it is KotlinTaskTestRun<*, *> }
.mapNotNull { (it as KotlinTaskTestRun<*, *>).executionTask }
compilationTarget is KotlinWithJavaTarget<*> &&
name == KotlinCompilation.TEST_COMPILATION_NAME ->
listOfNotNull(tasks.locateTask(compilationTarget.testTaskName))
this is KotlinJvmAndroidCompilation -> when (androidVariant) {
is UnitTestVariant -> listOfNotNull(tasks.locateTask(lowerCamelCaseName("test", androidVariant.name)))
is TestVariant -> listOfNotNull((androidVariant as TestVariant).connectedInstrumentTestProvider)
else -> emptyList()
}
else -> emptyList()
}
if (testTaskList.isEmpty()) return null
return testTaskList
.singleOrNull()
?.map { task ->
val framework = when (task) {
is Test -> testFrameworkOf(task)
else -> // Android connected test tasks don't inherit from Test, but we use JUnit for them
KotlinTestJvmFramework.junit
}
"$KOTLIN_MODULE_GROUP:$KOTLIN_TEST_ROOT_MODULE_NAME-framework-$framework"
}
}
internal const val KOTLIN_TEST_ROOT_MODULE_NAME = "kotlin-test"
private enum class KotlinTestJvmFramework {
junit, testng, junit5
}
private fun testFrameworkOf(testTask: Test): KotlinTestJvmFramework = when (testTask.options) {
is JUnitOptions -> KotlinTestJvmFramework.junit
is JUnitPlatformOptions -> KotlinTestJvmFramework.junit5
is TestNGOptions -> KotlinTestJvmFramework.testng
else -> // failed to detect, fallback to junit
KotlinTestJvmFramework.junit
}
private fun KotlinTargetWithTests<*, *>.findTestRunsByCompilation(
byCompilation: KotlinCompilation<*>
): NamedDomainObjectSet<out KotlinTargetTestRun<*>> {
fun KotlinExecution.ExecutionSource.isProducedFromTheCompilation(): Boolean = when (this) {
is CompilationExecutionSource<*> -> compilation == byCompilation
is JvmCompilationsTestRunSource -> byCompilation in testCompilations
is KotlinAggregateExecutionSource<*> -> this.executionSources.any { it.isProducedFromTheCompilation() }
else -> false
}
return testRuns.matching { it.executionSource.isProducedFromTheCompilation() }
}
//endregion
internal fun DependencyHandler.kotlinDependency(moduleName: String, versionOrNull: String?) =
create("$KOTLIN_MODULE_GROUP:$moduleName${versionOrNull?.prependIndent(":").orEmpty()}")
private fun Configuration.allNonProjectDependencies() = allDependencies.matching { it !is ProjectDependency }
internal fun Configuration.allNonProjectDependencies() = allDependencies.matching { it !is ProjectDependency }
@@ -0,0 +1,214 @@
/*
* 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 org.jetbrains.kotlin.gradle.internal
import com.android.build.gradle.api.TestVariant
import com.android.build.gradle.api.UnitTestVariant
import org.gradle.api.NamedDomainObjectSet
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.ConfigurationContainer
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.ExternalDependency
import org.gradle.api.artifacts.dsl.DependencyHandler
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.TaskContainer
import org.gradle.api.tasks.TaskProvider
import org.gradle.api.tasks.testing.Test
import org.gradle.api.tasks.testing.junit.JUnitOptions
import org.gradle.api.tasks.testing.junitplatform.JUnitPlatformOptions
import org.gradle.api.tasks.testing.testng.TestNGOptions
import org.jetbrains.kotlin.gradle.dsl.KotlinJsProjectExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinSingleTargetExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinTopLevelExtension
import org.jetbrains.kotlin.gradle.execution.KotlinAggregateExecutionSource
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmAndroidCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinWithJavaTarget
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope
import org.jetbrains.kotlin.gradle.plugin.sources.sourceSetDependencyConfigurationByScope
import org.jetbrains.kotlin.gradle.targets.js.npm.SemVer
import org.jetbrains.kotlin.gradle.targets.jvm.JvmCompilationsTestRunSource
import org.jetbrains.kotlin.gradle.tasks.locateTask
import org.jetbrains.kotlin.gradle.testing.KotlinTaskTestRun
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
private val Dependency.isKotlinTestRootDependency: Boolean
get() = group == KOTLIN_MODULE_GROUP && name == KOTLIN_TEST_ROOT_MODULE_NAME
private val kotlin150Version = SemVer(1.toBigInteger(), 5.toBigInteger(), 0.toBigInteger())
private fun isAtLeast1_5(version: String) = SemVer.from(version) >= kotlin150Version
private val jvmPlatforms = setOf(KotlinPlatformType.jvm, KotlinPlatformType.androidJvm)
internal fun Project.configureKotlinTestDependency(
topLevelExtension: KotlinTopLevelExtension,
coreLibrariesVersion: Provider<String>,
) {
when (topLevelExtension) {
is KotlinJsProjectExtension -> topLevelExtension.registerTargetObserver { target ->
target?.configureKotlinTestDependency(
configurations,
coreLibrariesVersion,
dependencies,
tasks
)
}
is KotlinSingleTargetExtension<*> -> topLevelExtension.target.configureKotlinTestDependency(
configurations,
coreLibrariesVersion,
dependencies,
tasks
)
is KotlinMultiplatformExtension -> topLevelExtension.targets.configureEach { target ->
target.configureKotlinTestDependency(
configurations,
coreLibrariesVersion,
dependencies,
tasks
)
}
}
}
private fun KotlinTarget.configureKotlinTestDependency(
configurations: ConfigurationContainer,
coreLibrariesVersion: Provider<String>,
dependencyHandler: DependencyHandler,
tasks: TaskContainer
) {
compilations.configureEach { compilation ->
val platformType = compilation.platformType
if (platformType in jvmPlatforms) {
// Checking dependencies which were added via dependsOn(KotlinSourceSet) call
// Compilation has own configurations for these that are different from KotlinSourceSet configurations
KotlinDependencyScope.values()
.map { configurations.sourceSetDependencyConfigurationByScope(compilation, it) }
.forEach {
it.maybeAddTestDependencyCapability(
compilation,
coreLibrariesVersion,
dependencyHandler,
tasks
)
}
compilation.kotlinSourceSets.forEach { sourceSet ->
KotlinDependencyScope.values()
.map { configurations.sourceSetDependencyConfigurationByScope(sourceSet, it) }
.forEach {
it.maybeAddTestDependencyCapability(
compilation,
coreLibrariesVersion,
dependencyHandler,
tasks
)
}
}
}
}
}
private fun Configuration.maybeAddTestDependencyCapability(
compilation: KotlinCompilation<*>,
coreLibrariesVersion: Provider<String>,
dependencyHandler: DependencyHandler,
tasks: TaskContainer
) {
withDependencies { dependencies ->
val testRootDependency = allNonProjectDependencies()
.singleOrNull { it.isKotlinTestRootDependency }
if (testRootDependency != null) {
val depVersion = testRootDependency.version ?: coreLibrariesVersion.get()
if (!isAtLeast1_5(depVersion)) return@withDependencies
val testCapability = compilation.kotlinTestCapabilityForJvmSourceSet(tasks)
if (testCapability != null) {
dependencies.addLater(
testCapability.map { capability ->
dependencyHandler
.kotlinDependency(KOTLIN_TEST_ROOT_MODULE_NAME, depVersion)
.apply {
(this as ExternalDependency).capabilities {
it.requireCapability(capability)
}
}
}
)
}
}
}
}
private fun KotlinCompilation<*>.kotlinTestCapabilityForJvmSourceSet(
tasks: TaskContainer,
): Provider<String>? {
val compilationTarget = target
val testTaskList: List<TaskProvider<out Task>> = when {
compilationTarget is KotlinTargetWithTests<*, *> -> compilationTarget
.findTestRunsByCompilation(this)
.matching { it is KotlinTaskTestRun<*, *> }
.mapNotNull { (it as KotlinTaskTestRun<*, *>).executionTask }
compilationTarget is KotlinWithJavaTarget<*> &&
name == KotlinCompilation.TEST_COMPILATION_NAME ->
listOfNotNull(tasks.locateTask(compilationTarget.testTaskName))
this is KotlinJvmAndroidCompilation -> when (androidVariant) {
is UnitTestVariant -> listOfNotNull(tasks.locateTask(lowerCamelCaseName("test", androidVariant.name)))
is TestVariant -> listOfNotNull((androidVariant as TestVariant).connectedInstrumentTestProvider)
else -> emptyList()
}
else -> emptyList()
}
if (testTaskList.isEmpty()) return null
return testTaskList
.singleOrNull()
?.map { task ->
val framework = when (task) {
is Test -> testFrameworkOf(task)
else -> // Android connected test tasks don't inherit from Test, but we use JUnit for them
KotlinTestJvmFramework.junit
}
"$KOTLIN_MODULE_GROUP:$KOTLIN_TEST_ROOT_MODULE_NAME-framework-$framework"
}
}
internal const val KOTLIN_TEST_ROOT_MODULE_NAME = "kotlin-test"
private enum class KotlinTestJvmFramework {
junit, testng, junit5
}
private fun testFrameworkOf(testTask: Test): KotlinTestJvmFramework = when (testTask.options) {
is JUnitOptions -> KotlinTestJvmFramework.junit
is JUnitPlatformOptions -> KotlinTestJvmFramework.junit5
is TestNGOptions -> KotlinTestJvmFramework.testng
else -> // failed to detect, fallback to junit
KotlinTestJvmFramework.junit
}
private fun KotlinTargetWithTests<*, *>.findTestRunsByCompilation(
byCompilation: KotlinCompilation<*>
): NamedDomainObjectSet<out KotlinTargetTestRun<*>> {
fun KotlinExecution.ExecutionSource.isProducedFromTheCompilation(): Boolean = when (this) {
is CompilationExecutionSource<*> -> compilation == byCompilation
is JvmCompilationsTestRunSource -> byCompilation in testCompilations
is KotlinAggregateExecutionSource<*> -> this.executionSources.any { it.isProducedFromTheCompilation() }
else -> false
}
return testRuns.matching { it.executionSource.isProducedFromTheCompilation() }
}
@@ -0,0 +1,198 @@
/*
* 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 org.jetbrains.kotlin.gradle.internal
import org.gradle.api.Project
import org.gradle.api.artifacts.ConfigurationContainer
import org.gradle.api.artifacts.dsl.DependencyHandler
import org.gradle.api.provider.Provider
import org.jetbrains.kotlin.gradle.dsl.KotlinJsProjectExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinSingleTargetExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinTopLevelExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.isTest
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.GradleKpmFragment
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.GradleKpmModule
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.hasKpmModel
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.kpmModules
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope
import org.jetbrains.kotlin.gradle.plugin.sources.android.AndroidBaseSourceSetName
import org.jetbrains.kotlin.gradle.plugin.sources.android.AndroidVariantType
import org.jetbrains.kotlin.gradle.plugin.sources.android.androidSourceSetInfoOrNull
import org.jetbrains.kotlin.gradle.plugin.sources.dependsOnClosure
import org.jetbrains.kotlin.gradle.plugin.sources.sourceSetDependencyConfigurationByScope
import org.jetbrains.kotlin.gradle.targets.js.npm.SemVer
internal fun Project.configureStdlibDefaultDependency(
topLevelExtension: KotlinTopLevelExtension,
coreLibrariesVersion: Provider<String>
) {
when {
project.hasKpmModel -> addStdlibToKpmProject(project, coreLibrariesVersion)
topLevelExtension is KotlinJsProjectExtension -> topLevelExtension.registerTargetObserver { target ->
target?.addStdlibDependency(configurations, dependencies, coreLibrariesVersion)
}
topLevelExtension is KotlinSingleTargetExtension<*> -> topLevelExtension
.target
.addStdlibDependency(configurations, dependencies, coreLibrariesVersion)
topLevelExtension is KotlinMultiplatformExtension -> topLevelExtension
.targets
.configureEach { target ->
target.addStdlibDependency(configurations, dependencies, coreLibrariesVersion)
}
}
}
private fun addStdlibToKpmProject(
project: Project,
coreLibrariesVersion: Provider<String>
) {
project.kpmModules.named(GradleKpmModule.MAIN_MODULE_NAME) { main ->
main.fragments.named(GradleKpmFragment.COMMON_FRAGMENT_NAME) { common ->
common.dependencies {
api(project.dependencies.kotlinDependency("kotlin-stdlib-common", coreLibrariesVersion.get()))
}
}
main.variants.configureEach { variant ->
val dependencyHandler = project.dependencies
val stdlibModule = when (variant.platformType) {
KotlinPlatformType.common -> error("variants are not expected to be common")
KotlinPlatformType.jvm -> chooseStdlibJvmDependency(coreLibrariesVersion)
KotlinPlatformType.js -> "kotlin-stdlib-js"
KotlinPlatformType.wasm -> "kotlin-stdlib-wasm"
KotlinPlatformType.androidJvm -> null // TODO: expect support on the AGP side?
KotlinPlatformType.native -> null
}
if (stdlibModule != null) {
variant.dependencies {
api(dependencyHandler.kotlinDependency(stdlibModule, coreLibrariesVersion.get()))
}
}
}
}
}
private fun KotlinTarget.addStdlibDependency(
configurations: ConfigurationContainer,
dependencies: DependencyHandler,
coreLibrariesVersion: Provider<String>
) {
compilations.configureEach { compilation ->
compilation.allKotlinSourceSets.forEach { kotlinSourceSet ->
val scope = if (compilation.isTest() ||
(this is KotlinAndroidTarget &&
kotlinSourceSet.isRelatedToAndroidTestSourceSet()
)
) {
KotlinDependencyScope.IMPLEMENTATION_SCOPE
} else {
KotlinDependencyScope.API_SCOPE
}
val scopeConfiguration = configurations
.sourceSetDependencyConfigurationByScope(kotlinSourceSet, scope)
scopeConfiguration.withDependencies { dependencySet ->
// Check if stdlib is directly added to SourceSet
if (isStdlibAddedByUser(configurations, stdlibModules, kotlinSourceSet)) return@withDependencies
val stdlibModule = compilation
.platformType
.stdlibPlatformType(coreLibrariesVersion, this, kotlinSourceSet)
?: return@withDependencies
// Check if stdlib module is added to SourceSets hierarchy
if (
isStdlibAddedByUser(
configurations,
setOf(stdlibModule),
*kotlinSourceSet.dependsOnClosure.toTypedArray()
)
) return@withDependencies
dependencySet.addLater(
coreLibrariesVersion.map {
dependencies.kotlinDependency(stdlibModule, it)
}
)
}
}
}
}
private fun isStdlibAddedByUser(
configurations: ConfigurationContainer,
stdlibModules: Set<String>,
vararg sourceSets: KotlinSourceSet
): Boolean {
return sourceSets
.asSequence()
.flatMap { sourceSet ->
KotlinDependencyScope.values().map { scope ->
configurations.sourceSetDependencyConfigurationByScope(sourceSet, scope)
}.asSequence()
}
.flatMap { it.allNonProjectDependencies().asSequence() }
.any { dependency ->
dependency.group == KOTLIN_MODULE_GROUP && dependency.name in stdlibModules
}
}
private fun KotlinPlatformType.stdlibPlatformType(
coreLibrariesVersion: Provider<String>,
kotlinTarget: KotlinTarget,
kotlinSourceSet: KotlinSourceSet
): String? = when (this) {
KotlinPlatformType.jvm -> chooseStdlibJvmDependency(coreLibrariesVersion)
KotlinPlatformType.androidJvm -> {
if (kotlinTarget is KotlinAndroidTarget &&
kotlinSourceSet.androidSourceSetInfoOrNull?.androidSourceSetName == AndroidBaseSourceSetName.Main.name
) {
chooseStdlibJvmDependency(coreLibrariesVersion)
} else {
null
}
}
KotlinPlatformType.js -> "kotlin-stdlib-js"
KotlinPlatformType.wasm -> "kotlin-stdlib-wasm"
KotlinPlatformType.native -> null
KotlinPlatformType.common -> // there's no platform compilation that the source set is default for
"kotlin-stdlib-common"
}
private val kotlin180Version = SemVer(1.toBigInteger(), 8.toBigInteger(), 0.toBigInteger())
private fun chooseStdlibJvmDependency(
coreLibrariesVersion: Provider<String>
): String {
// Current 'SemVer.satisfies' release always returns `false` for any "-SNAPSHOT" version.
return if (SemVer.from(coreLibrariesVersion.get()) < kotlin180Version) {
"kotlin-stdlib-jdk8"
} else {
"kotlin-stdlib"
}
}
private val androidTestVariants = setOf(AndroidVariantType.UnitTest, AndroidVariantType.InstrumentedTest)
private fun KotlinSourceSet.isRelatedToAndroidTestSourceSet(): Boolean {
val androidVariant = androidSourceSetInfoOrNull?.androidVariantType ?: return false
return androidVariant in androidTestVariants
}
private val stdlibModules = setOf(
"kotlin-stdlib-common",
"kotlin-stdlib",
"kotlin-stdlib-jdk7",
"kotlin-stdlib-jdk8",
"kotlin-stdlib-js"
)