Split dependency management code into 3 files
^KT-41642 Fixed
This commit is contained in:
+1
-1
@@ -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)"""
|
||||
|
||||
+1
-2
@@ -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 ->
|
||||
|
||||
+1
-368
@@ -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 }
|
||||
|
||||
+214
@@ -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() }
|
||||
}
|
||||
+198
@@ -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"
|
||||
)
|
||||
Reference in New Issue
Block a user