[Gradle] IdeaKotlinBinaryCoordinates: Include 'Capabilities'

... in order to differentiate between feature variants of
published libraries (e.g. testFixtures)

^KT-60053 In Progress
This commit is contained in:
Sebastian Sellmair
2023-07-10 14:40:49 +02:00
committed by Space Team
parent 9732651264
commit e3f0fa4257
16 changed files with 1879 additions and 21 deletions
@@ -298,17 +298,35 @@ public final class org/jetbrains/kotlin/gradle/idea/serialize/IdeaKotlinStringEx
public fun serialize (Lorg/jetbrains/kotlin/gradle/idea/serialize/IdeaKotlinSerializationContext;Ljava/lang/String;)[B
}
public final class org/jetbrains/kotlin/gradle/idea/tcs/IdeaKotlinBinaryCapability : java/io/Serializable {
public static final field serialVersionUID J
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lorg/jetbrains/kotlin/gradle/idea/tcs/IdeaKotlinBinaryCapability;
public static synthetic fun copy$default (Lorg/jetbrains/kotlin/gradle/idea/tcs/IdeaKotlinBinaryCapability;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lorg/jetbrains/kotlin/gradle/idea/tcs/IdeaKotlinBinaryCapability;
public fun equals (Ljava/lang/Object;)Z
public final fun getGroup ()Ljava/lang/String;
public final fun getName ()Ljava/lang/String;
public final fun getVersion ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final class org/jetbrains/kotlin/gradle/idea/tcs/IdeaKotlinBinaryCoordinates : org/jetbrains/kotlin/gradle/idea/tcs/IdeaKotlinDependencyCoordinates {
public static final field serialVersionUID J
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()Ljava/lang/String;
public final fun component3 ()Ljava/lang/String;
public final fun component4 ()Ljava/lang/String;
public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lorg/jetbrains/kotlin/gradle/idea/tcs/IdeaKotlinBinaryCoordinates;
public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)Lorg/jetbrains/kotlin/gradle/idea/tcs/IdeaKotlinBinaryCoordinates;
public static synthetic fun copy$default (Lorg/jetbrains/kotlin/gradle/idea/tcs/IdeaKotlinBinaryCoordinates;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lorg/jetbrains/kotlin/gradle/idea/tcs/IdeaKotlinBinaryCoordinates;
public static synthetic fun copy$default (Lorg/jetbrains/kotlin/gradle/idea/tcs/IdeaKotlinBinaryCoordinates;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILjava/lang/Object;)Lorg/jetbrains/kotlin/gradle/idea/tcs/IdeaKotlinBinaryCoordinates;
public fun equals (Ljava/lang/Object;)Z
public final fun getCapabilities ()Ljava/util/Set;
public final fun getGroup ()Ljava/lang/String;
public final fun getModule ()Ljava/lang/String;
public final fun getSourceSetName ()Ljava/lang/String;
@@ -0,0 +1,63 @@
/*
* Copyright 2010-2023 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.idea.tcs
import java.io.Serializable
/**
* Several variants can be published under a given set of maven coordinates (group, module, version),
* Capabilities can classify those variants in a way that e.g. two variants with the same maven coordinates
* can be resolved next to each other.
*
* A classical example for this would be resolving the 'main' (aka 'production') variant alongside
* the corresponding "testFixtures".
*
* See:
* - https://docs.gradle.org/current/userguide/feature_variants.html
* - https://docs.gradle.org/current/userguide/component_capabilities.html
* @since 1.9.20
*/
@IdeaKotlinModel
class IdeaKotlinBinaryCapability(
val group: String,
val name: String,
val version: String?,
) : Serializable {
internal companion object {
const val serialVersionUID = 0L
}
override fun toString(): String {
return buildString {
append("$group:$name")
if (version != null) append(":$version")
}
}
fun copy(
group: String = this.group,
name: String = this.name,
version: String? = this.version,
): IdeaKotlinBinaryCapability {
return IdeaKotlinBinaryCapability(group = group, name = name, version = version)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is IdeaKotlinBinaryCapability) return false
if (group != other.group) return false
if (name != other.name) return false
if (version != other.version) return false
return true
}
override fun hashCode(): Int {
var result = group.hashCode()
result = 31 * result + name.hashCode()
result = 31 * result + (version?.hashCode() ?: 0)
return result
}
}
@@ -3,6 +3,8 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@file:Suppress("ConstPropertyName", "DeprecatedCallableAddReplaceWith")
package org.jetbrains.kotlin.gradle.idea.tcs
import java.io.Serializable
@@ -10,14 +12,174 @@ import java.io.Serializable
@IdeaKotlinModel
sealed interface IdeaKotlinDependencyCoordinates : Serializable
data class IdeaKotlinBinaryCoordinates(
class IdeaKotlinBinaryCoordinates(
val group: String,
val module: String,
val version: String?,
val sourceSetName: String? = null,
/**
* @see IdeaKotlinBinaryCapability
* @since 1.9.20
*/
val capabilities: Set<IdeaKotlinBinaryCapability> = emptySet(),
) : IdeaKotlinDependencyCoordinates {
override fun toString(): String {
return "$group:$module:$version${sourceSetName?.let { ":$it" }.orEmpty()}"
constructor(
group: String,
module: String,
version: String?,
sourceSetName: String? = null,
) : this(
group = group,
module = module,
version = version,
sourceSetName = sourceSetName,
capabilities = emptySet()
)
/**
* String that can be used to identify the coordinates with the following contract:
* All coordinates that are equal will have an equal identityString
* All coordinates that are not equal will have a non equal identityString
*
* This, however, shall not be shown to users, as the String is hard to read
* The identityString is not stable across Kotlin versions, hence not intended to be parsed.
*/
val identityString = buildString {
append("$group:$module")
if (version != null) append(":$version")
if (capabilities.isNotEmpty()) {
append(capabilities.joinToString(", ", "(", ")"))
}
}
/**
* String intended to be shown to users. E.g. a library within the IDE can use
* this String to show the coordinates to a given user.
*
* This will try to be as useful as possible, implementing a notion of 'classifying capabilities'
*
* Example, testFixtures from Gradle:
* Such testFixtures will be resolved with coordinates like
* ```kotlin
* IdeaKotlinBinaryCoordinates(
* group = "org.jetbrains",
* module = "sample",
* version = "1.0.0,
* capabilities = setOf(
* IdeaKotlinBinaryCapability(
* group = "org.jetbrains",
* name = "sample-test-fixtures, /* <--- See the classifying 'test-fixtures' suffix */
* version = "1.0.0"
* )
* )
* )
* ```
*
* Such coordinates will be displayed using their classifiers like
* "org.jetbrains:sample-test-fixtures:1.0.0"
*
*/
val displayString = run {
val classifyingCapabilities = capabilities.filter { capability -> capability.group == group && capability.name.startsWith(module) }
when {
classifyingCapabilities.size == 1 -> classifyingCapabilities.single().toString()
classifyingCapabilities.size > 1 -> buildString {
append(group)
append(classifyingCapabilities.joinToString(prefix = "(", postfix = ")", separator = ", ") { capability ->
capability.name.removePrefix(group)
})
}
else -> buildString {
append("$group:$module")
if (version != null) append(":$version")
if (sourceSetName != null) append(":$sourceSetName")
}
}
}
override fun toString(): String = displayString
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is IdeaKotlinBinaryCoordinates) return false
if (group != other.group) return false
if (module != other.module) return false
if (version != other.version) return false
if (sourceSetName != other.sourceSetName) return false
if (capabilities != other.capabilities) return false
return true
}
override fun hashCode(): Int {
var result = group.hashCode()
result = 31 * result + module.hashCode()
result = 31 * result + (version?.hashCode() ?: 0)
result = 31 * result + (sourceSetName?.hashCode() ?: 0)
result = 31 * result + capabilities.hashCode()
return result
}
/*
Keep compatibility with 1.9.0 and lower
*/
fun copy(
group: String = this.group,
module: String = this.module,
version: String? = this.version,
sourceSetName: String? = this.sourceSetName,
): IdeaKotlinBinaryCoordinates {
return IdeaKotlinBinaryCoordinates(
group = group,
module = module,
version = version,
sourceSetName = sourceSetName,
capabilities = capabilities
)
}
fun copy(
group: String = this.group,
module: String = this.module,
version: String? = this.version,
sourceSetName: String? = this.sourceSetName,
capabilities: Set<IdeaKotlinBinaryCapability> = this.capabilities,
): IdeaKotlinBinaryCoordinates {
return IdeaKotlinBinaryCoordinates(
group = group,
module = module,
version = version,
sourceSetName = sourceSetName,
capabilities = capabilities
)
}
@Deprecated("Since 1.9.20")
operator fun component1(): String = this.group
@Deprecated("Since 1.9.20")
operator fun component2(): String = this.module
@Deprecated("Since 1.9.20")
operator fun component3(): String? = this.version
@Deprecated("Since 1.9.20")
operator fun component4(): String? = this.sourceSetName
/**
* In order to keep java.io.Serializable implementation backwards compatible:
* 'capabilities' was added in 1.9.20. If a binary produced before 1.9.20 gets deserialized, then 'capabilities'
* will be 'null'. In this case we use the 'copy' function to provide an instance that will have an emptySet instead.
*/
private fun readResolve(): Any {
@Suppress("SENSELESS_COMPARISON")
if (capabilities == null) {
return copy(capabilities = emptySet())
}
return this
}
internal companion object {
@@ -28,7 +190,7 @@ data class IdeaKotlinBinaryCoordinates(
data class IdeaKotlinProjectCoordinates(
val buildId: String,
val projectPath: String,
val projectName: String
val projectName: String,
) : Serializable, IdeaKotlinDependencyCoordinates {
override fun toString(): String {
return "${buildId.takeIf { it != ":" }?.plus(":").orEmpty()}$projectPath"
@@ -41,7 +203,7 @@ data class IdeaKotlinProjectCoordinates(
data class IdeaKotlinSourceCoordinates(
val project: IdeaKotlinProjectCoordinates,
val sourceSetName: String
val sourceSetName: String,
) : IdeaKotlinDependencyCoordinates {
val buildId: String get() = project.buildId