All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.jetbrains.kotlin.gradle.internal.KotlinDependenciesManagement.kt Maven / Gradle / Ivy
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.gradle.internal
import com.android.build.gradle.TestedExtension
import com.android.build.gradle.api.TestVariant
import com.android.build.gradle.api.UnitTestVariant
import org.gradle.api.InvalidUserDataException
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.*
import org.gradle.api.tasks.testing.AbstractTestTask
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.KotlinGradleFragment
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleModule
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20ProjectExtension
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope
import org.jetbrains.kotlin.gradle.plugin.sources.resolveAllDependsOnSourceSets
import org.jetbrains.kotlin.gradle.plugin.sources.sourceSetDependencyConfigurationByScope
import org.jetbrains.kotlin.gradle.plugin.sources.withAllDependsOnSourceSets
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
internal const val KOTLIN_MODULE_GROUP = "org.jetbrains.kotlin"
internal const val KOTLIN_COMPILER_EMBEDDABLE = "kotlin-compiler-embeddable"
internal fun customizeKotlinDependencies(project: Project) {
configureStdlibDefaultDependency(project)
if (project.topLevelExtension is KotlinProjectExtension) { // TODO: extend this logic to PM20
configureKotlinTestDependency(project)
}
configureDefaultVersionsResolutionStrategy(project)
excludeStdlibCommonFromPlatformCompilations(project)
}
private fun configureDefaultVersionsResolutionStrategy(project: Project) {
project.configurations.all { configuration ->
// Use the API introduced in Gradle 4.4 to modify the dependencies directly before they are resolved:
configuration.withDependencies { dependencySet ->
val coreLibrariesVersion = project.topLevelExtension.coreLibrariesVersion
dependencySet.filterIsInstance()
.filter { it.group == KOTLIN_MODULE_GROUP && it.version.isNullOrEmpty() }
.forEach { it.version { constraint -> constraint.require(coreLibrariesVersion) } }
}
}
}
//region stdlib
private fun excludeStdlibCommonFromPlatformCompilations(project: Project) {
val multiplatformExtension = project.multiplatformExtensionOrNull ?: return
multiplatformExtension.targets.matching { it !is KotlinMetadataTarget }.all {
it.excludeStdlibCommonFromPlatformCompilations()
}
}
// there several JVM-like targets, like KotlinWithJava, or KotlinAndroid, and they don't have common supertype
// aside from KotlinTarget
private fun KotlinTarget.excludeStdlibCommonFromPlatformCompilations() {
compilations.all {
listOfNotNull(
it.compileDependencyConfigurationName,
it.defaultSourceSet.apiMetadataConfigurationName,
it.defaultSourceSet.implementationMetadataConfigurationName,
(it as? KotlinCompilationToRunnableFiles<*>)?.runtimeDependencyConfigurationName,
// Additional configurations for (old) jvmWithJava-preset. Remove it when we drop it completely
(it as? KotlinWithJavaCompilation<*>)?.apiConfigurationName
).forEach { configurationName ->
project.configurations.getByName(configurationName).exclude(
mapOf("group" to "org.jetbrains.kotlin", "module" to "kotlin-stdlib-common")
)
}
}
}
internal fun configureStdlibDefaultDependency(project: Project) = with(project) {
if (!PropertiesProvider(project).stdlibDefaultDependency)
return
val scopesToHandleConfigurations = listOf(KotlinDependencyScope.API_SCOPE, KotlinDependencyScope.IMPLEMENTATION_SCOPE)
when (val topLevelExtension = project.topLevelExtension) {
is KotlinPm20ProjectExtension -> {
addStdlibToPm20Project(topLevelExtension)
}
is KotlinProjectExtension -> {
topLevelExtension.sourceSets.all { kotlinSourceSet ->
scopesToHandleConfigurations.forEach { scope ->
val scopeConfiguration = project.sourceSetDependencyConfigurationByScope(kotlinSourceSet, scope)
project.tryWithDependenciesIfUnresolved(scopeConfiguration) { dependencies ->
val scopeToAddStdlibDependency =
if (isRelatedToAndroidTestSourceSet(
project,
kotlinSourceSet
)
) // AGP deprecates API configurations in test source sets
KotlinDependencyScope.IMPLEMENTATION_SCOPE
else KotlinDependencyScope.API_SCOPE
if (scope != scopeToAddStdlibDependency)
return@tryWithDependenciesIfUnresolved
chooseAndAddStdlibDependency(project, kotlinSourceSet, dependencies)
}
}
}
}
}
}
private fun addStdlibToPm20Project(
topLevelExtension: KotlinPm20ProjectExtension
) {
val project = topLevelExtension.project
topLevelExtension.modules.matching { it.name == KotlinGradleModule.MAIN_MODULE_NAME }.configureEach { main ->
main.fragments.matching { it.name == KotlinGradleFragment.COMMON_FRAGMENT_NAME }.configureEach { common ->
common.dependencies {
api(project.kotlinDependency("kotlin-stdlib-common", project.topLevelExtension.coreLibrariesVersion))
}
}
main.variants.configureEach { variant ->
val dependency = when (variant.platformType) {
KotlinPlatformType.common -> error("variants are not expected to be common")
KotlinPlatformType.jvm -> "kotlin-stdlib" // TODO get JDK from JVM variants
KotlinPlatformType.js -> "kotlin-stdlib-js"
KotlinPlatformType.androidJvm -> null // TODO: expect support on the AGP side?
KotlinPlatformType.native -> null
}
if (dependency != null) {
variant.dependencies {
api(project.kotlinDependency(dependency, project.topLevelExtension.coreLibrariesVersion))
}
}
}
}
}
private fun chooseAndAddStdlibDependency(
project: Project,
kotlinSourceSet: KotlinSourceSet,
dependencies: DependencySet
) {
val sourceSetDependencyConfigurations =
KotlinDependencyScope.values().map { project.sourceSetDependencyConfigurationByScope(kotlinSourceSet, it) }
val hierarchySourceSetsDependencyConfigurations = kotlinSourceSet.resolveAllDependsOnSourceSets().flatMap { hierarchySourceSet ->
KotlinDependencyScope.values().map { scope ->
project.sourceSetDependencyConfigurationByScope(hierarchySourceSet, scope)
}
}
val compilations = CompilationSourceSetUtil.compilationsBySourceSets(project).getValue(kotlinSourceSet)
.filter { it.target !is KotlinMetadataTarget }
if (compilations.isEmpty())
// source set doesn't take part in any compilation; prohibit this in the future; also, this caused KT-40559 in Android libraries
return
val platformTypes = compilations.mapTo(mutableSetOf()) { it.platformType }
val sourceSetStdlibPlatformType = when {
platformTypes.size == 1 -> platformTypes.single()
setOf(KotlinPlatformType.jvm, KotlinPlatformType.androidJvm).containsAll(platformTypes) -> KotlinPlatformType.jvm
else -> KotlinPlatformType.common
}
val isStdlibAddedByUser = sourceSetDependencyConfigurations
.flatMap { it.allNonProjectDependencies() }
.any { dependency -> dependency.group == KOTLIN_MODULE_GROUP && dependency.name in stdlibModules }
if (!isStdlibAddedByUser) {
val stdlibModuleName = when (sourceSetStdlibPlatformType) {
KotlinPlatformType.jvm -> stdlibModuleForJvmCompilations(compilations)
KotlinPlatformType.androidJvm ->
if (kotlinSourceSet.name == androidMainSourceSetName(project)) stdlibModuleForJvmCompilations(compilations) else null
KotlinPlatformType.js -> "kotlin-stdlib-js"
KotlinPlatformType.native -> null
KotlinPlatformType.common -> // there's no platform compilation that the source set is default for
"kotlin-stdlib-common"
}
// If the exact same module is added in the source sets hierarchy, possibly even with a different scope, we don't add it
val moduleAddedInHierarchy = hierarchySourceSetsDependencyConfigurations.any {
it.allNonProjectDependencies()
.any { dependency -> dependency.group == KOTLIN_MODULE_GROUP && dependency.name == stdlibModuleName }
}
if (stdlibModuleName != null && !moduleAddedInHierarchy)
dependencies.add(project.kotlinDependency(stdlibModuleName, project.kotlinExtension.coreLibrariesVersion))
}
}
private fun Project.findAndroidTarget(): KotlinAndroidTarget? {
val kotlinExtension = project.kotlinExtension
return when (kotlinExtension) {
is KotlinMultiplatformExtension -> kotlinExtension.targets.withType(KotlinAndroidTarget::class.java).single()
is KotlinAndroidProjectExtension -> kotlinExtension.target
else -> null
}
}
private fun androidMainSourceSetName(project: Project): String {
val target = project.findAndroidTarget() ?: error("No Android target found")
return AbstractAndroidProjectHandler.kotlinSourceSetNameForAndroidSourceSet(target, "main")
}
private fun isRelatedToAndroidTestSourceSet(project: Project, kotlinSourceSet: KotlinSourceSet): Boolean {
val androidExtension = project.extensions.findByName("android") as? TestedExtension ?: return false
val androidTarget = project.findAndroidTarget() ?: return false
if (androidTarget.compilations.any {
(it.androidVariant is UnitTestVariant || it.androidVariant is TestVariant) && it.defaultSourceSet === kotlinSourceSet
}
) return true
(androidExtension.testVariants + androidExtension.unitTestVariants).forEach { variant ->
if (variant.sourceSets.any {
kotlinSourceSet.name == AbstractAndroidProjectHandler.kotlinSourceSetNameForAndroidSourceSet(androidTarget, it.name)
}
) return true
}
return false
}
private val stdlibModules = setOf("kotlin-stdlib-common", "kotlin-stdlib", "kotlin-stdlib-jdk7", "kotlin-stdlib-jdk8", "kotlin-stdlib-js")
private fun stdlibModuleForJvmCompilations(compilations: Iterable>): String {
val jvmTargets = compilations.map { (it.kotlinOptions as KotlinJvmOptions).jvmTarget }
return if ("1.6" in jvmTargets) "kotlin-stdlib" else "kotlin-stdlib-jdk8"
}
//endregion
//region kotlin-test
internal fun configureKotlinTestDependency(project: Project) {
if (!PropertiesProvider(project).kotlinTestInferJvmVariant)
return
fun isKotlinTestRootDependency(dependency: Dependency) =
dependency.group == KOTLIN_MODULE_GROUP && dependency.name == KOTLIN_TEST_ROOT_MODULE_NAME
val versionPrefixRegex = Regex("""^(\d+)\.(\d+)""")
fun isAtLeast1_5(version: String) = versionPrefixRegex.find(version)?.let {
val c1 = it.groupValues[1].toInt()
val c2 = it.groupValues[2].toInt()
c1 > 1 || c1 == 1 && c2 >= 5
} ?: false
KotlinDependencyScope.values().forEach { scope ->
val versionOrNullBySourceSet = mutableMapOf()
project.kotlinExtension.sourceSets.all { kotlinSourceSet ->
val configuration = project.sourceSetDependencyConfigurationByScope(kotlinSourceSet, scope)
var finalizingDependencies = false
configuration.nonProjectDependencies().matching(::isKotlinTestRootDependency).apply {
firstOrNull()?.let { versionOrNullBySourceSet[kotlinSourceSet] = it.version }
whenObjectRemoved {
if (!finalizingDependencies && !any())
versionOrNullBySourceSet.remove(kotlinSourceSet)
}
whenObjectAdded { item ->
versionOrNullBySourceSet[kotlinSourceSet] = item.version
}
}
project.tryWithDependenciesIfUnresolved(configuration) { dependencies ->
val parentOrOwnVersions: List =
kotlinSourceSet.withAllDependsOnSourceSets().filter(versionOrNullBySourceSet::contains)
.map(versionOrNullBySourceSet::get)
finalizingDependencies = true
for (version in parentOrOwnVersions.distinct()) { // add dependencies with each version and let Gradle disambiguate them
val effectiveVersion = version ?: project.kotlinExtension.coreLibrariesVersion
if (!isAtLeast1_5(effectiveVersion)) continue
val clarifyCapability = kotlinTestCapabilityForJvmSourceSet(project, kotlinSourceSet) ?: continue
val clarifiedDependency =
(project.kotlinDependency(KOTLIN_TEST_ROOT_MODULE_NAME, version) as ExternalDependency).apply {
if (version == null) {
version { constraint -> constraint.require(project.kotlinExtension.coreLibrariesVersion) }
}
capabilities {
it.requireCapability(clarifyCapability)
}
}
dependencies.add(clarifiedDependency)
}
}
}
}
}
private fun kotlinTestCapabilityForJvmSourceSet(project: Project, kotlinSourceSet: KotlinSourceSet): String? {
val compilations = CompilationSourceSetUtil.compilationsBySourceSets(project).getValue(kotlinSourceSet)
.filter { it.target !is KotlinMetadataTarget }
.ifEmpty { return null }
val platformTypes = compilations.map { it.platformType }
// TODO: Extract jvmPlatformTypes to public constant?
val jvmPlatforms = setOf(KotlinPlatformType.jvm, KotlinPlatformType.androidJvm)
if (!jvmPlatforms.containsAll(platformTypes)) return null
val testTaskLists: List?> = compilations.map { compilation ->
val target = compilation.target
when {
target is KotlinTargetWithTests<*, *> ->
target.findTestRunsByCompilation(compilation)?.filterIsInstance>()?.map { it.executionTask.get() }
target is KotlinWithJavaTarget<*> ->
if (compilation.name == KotlinCompilation.TEST_COMPILATION_NAME)
project.locateTask(target.testTaskName)?.get()?.let(::listOf)
else null
compilation is KotlinJvmAndroidCompilation -> when (compilation.androidVariant) {
is UnitTestVariant ->
project.locateTask(lowerCamelCaseName("test", compilation.androidVariant.name))?.get()?.let(::listOf)
is TestVariant -> (compilation.androidVariant as TestVariant).connectedInstrumentTestProvider.orNull?.let(::listOf)
else -> null
}
else -> null
}
}
if (null in testTaskLists) {
return null
}
val testTasks = testTaskLists.flatMap { checkNotNull(it) }
val frameworks = testTasks.mapTo(mutableSetOf()) { testTask ->
when (testTask) {
is Test -> testFrameworkOf(testTask)
else -> // Android connected test tasks don't inherit from Test, but we use JUnit for them
KotlinTestJvmFramework.junit
}
}
return "$KOTLIN_MODULE_GROUP:$KOTLIN_TEST_ROOT_MODULE_NAME-framework-${frameworks.singleOrNull() ?: return null}"
}
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<*>): List>? {
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.filter { it.executionSource.isProducedFromTheCompilation() }.takeIf { it.isNotEmpty() }
}
//endregion
internal fun Project.kotlinDependency(moduleName: String, versionOrNull: String?) =
project.dependencies.create("$KOTLIN_MODULE_GROUP:$moduleName${versionOrNull?.prependIndent(":").orEmpty()}")
private fun Project.tryWithDependenciesIfUnresolved(configuration: Configuration, action: (DependencySet) -> Unit) {
fun reportAlreadyResolved() {
logger.info("Could not setup Kotlin-specific dependencies for $configuration as it is already resolved")
}
if (configuration.state != Configuration.State.UNRESOLVED)
return reportAlreadyResolved()
// Gradle doesn't provide any public API to check if it's safe to modify a configuration's dependencies.
// The state of the configuration may still be UNRESOLVED but it might have 'participated' in dependency resolution and its dependencies
// may now be frozen.
try {
configuration.withDependencies(action)
} catch (e: InvalidUserDataException) {
reportAlreadyResolved()
}
}
private fun Configuration.allNonProjectDependencies() = allDependencies.matching { it !is ProjectDependency }
private fun Configuration.nonProjectDependencies() = dependencies.matching { it !is ProjectDependency }