Fix ivy descriptor and snapshot artifacts

This commit is contained in:
Tad Fisher
2023-12-19 17:40:34 -08:00
parent 8ceeeb9611
commit e83e42f9d4
11 changed files with 382 additions and 365 deletions

View File

@@ -1,26 +1,53 @@
package org.nixos.gradle2nix.model
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
@Serializable
@Serializable(DependencyCoordinates.Serializer::class)
data class DependencyCoordinates(
val group: String,
val module: String,
val version: String,
val timestamp: String? = null
) {
) : Comparable<DependencyCoordinates> {
override fun toString(): String = if (timestamp != null) {
"$group:$module:$version:$timestamp"
} else {
"$group:$module:$version"
}
val isSnapshot: Boolean get() = timestamp != null
val moduleVersion: String get() = version
val artifactVersion: String get() =
timestamp?.let { version.replace("SNAPSHOT", it) } ?: version
override fun compareTo(other: DependencyCoordinates): Int = comparator.compare(this, other)
object Serializer : KSerializer<DependencyCoordinates> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor(
DependencyCoordinates::class.qualifiedName!!,
PrimitiveKind.STRING
)
override fun deserialize(decoder: Decoder): DependencyCoordinates {
val encoded = decoder.decodeString()
return parse(encoded)
}
override fun serialize(encoder: Encoder, value: DependencyCoordinates) {
encoder.encodeString(value.toString())
}
}
companion object {
val comparator = compareBy<DependencyCoordinates> { it.group }
.thenBy { it.module }
.thenByDescending { it.artifactVersion }
fun parse(id: String): DependencyCoordinates {
val parts = id.split(":")
return when (parts.size) {

View File

@@ -13,7 +13,7 @@ data class ResolvedConfiguration(
allDependencies.add(component)
}
fun hasDependency(componentId: String): Boolean {
fun hasDependency(componentId: DependencyCoordinates): Boolean {
return allDependencies.any { it.id == componentId }
}
}

View File

@@ -4,10 +4,9 @@ import kotlinx.serialization.Serializable
@Serializable
data class ResolvedDependency(
val id: String,
val id: DependencyCoordinates,
val source: DependencySource,
val direct: Boolean,
val coordinates: DependencyCoordinates,
val repository: String?,
val dependencies: List<String> = emptyList(),
)

View File

@@ -0,0 +1,172 @@
package org.nixos.gradle2nix.model
import java.util.concurrent.ConcurrentHashMap
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
@Serializable(Version.Serializer::class)
class Version(val source: String, val parts: List<String>, base: Version?) : Comparable<Version> {
private val base: Version
val numericParts: List<Long?>
init {
this.base = base ?: this
this.numericParts = parts.map {
try { it.toLong() } catch (e: NumberFormatException) { null }
}
}
override fun compareTo(other: Version): Int = compare(this, other)
override fun toString(): String = source
override fun equals(other: Any?): Boolean = when {
other === this -> true
other == null || other !is Version -> false
else -> source == other.source
}
override fun hashCode(): Int = source.hashCode()
object Comparator : kotlin.Comparator<Version> {
override fun compare(o1: Version, o2: Version): Int =
Version.compare(o1, o2)
}
internal object Serializer : KSerializer<Version> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor(
Version::class.qualifiedName!!,
PrimitiveKind.STRING
)
override fun serialize(encoder: Encoder, value: Version) {
encoder.encodeString(value.source)
}
override fun deserialize(decoder: Decoder): Version {
return Version(decoder.decodeString())
}
}
companion object {
private val SPECIAL_MEANINGS: Map<String, Int> = mapOf(
"dev" to -1,
"rc" to 1,
"snapshot" to 2,
"final" to 3,
"ga" to 4,
"release" to 5,
"sp" to 6
)
private val cache = ConcurrentHashMap<String, Version>()
// From org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionParser
operator fun invoke(original: String): Version = cache.getOrPut(original) {
val parts = mutableListOf<String>()
var digit = false
var startPart = 0
var pos = 0
var endBase = 0
var endBaseStr = 0
while (pos < original.length) {
val ch = original[pos]
if (ch == '.' || ch == '_' || ch == '-' || ch == '+') {
parts.add(original.substring(startPart, pos))
startPart = pos + 1
digit = false
if (ch != '.' && endBaseStr == 0) {
endBase = parts.size
endBaseStr = pos
}
} else if (ch in '0'..'9') {
if (!digit && pos > startPart) {
if (endBaseStr == 0) {
endBase = parts.size + 1
endBaseStr = pos
}
parts.add(original.substring(startPart, pos))
startPart = pos
}
digit = true
} else {
if (digit) {
if (endBaseStr == 0) {
endBase = parts.size + 1
endBaseStr = pos
}
parts.add(original.substring(startPart, pos))
startPart = pos
}
digit = false
}
pos++
}
if (pos > startPart) {
parts.add(original.substring(startPart, pos))
}
var base: Version? = null
if (endBaseStr > 0) {
base = Version(original.substring(0, endBaseStr), parts.subList(0, endBase), null)
}
Version(original, parts, base)
}
// From org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.StaticVersionComparator
private fun compare(version1: Version, version2: Version): Int {
if (version1 == version2) {
return 0
}
val parts1 = version1.parts
val parts2 = version2.parts
val numericParts1 = version1.numericParts
val numericParts2 = version2.numericParts
var lastIndex = -1
for (i in 0..<(minOf(parts1.size, parts2.size))) {
lastIndex = i
val part1 = parts1[i]
val part2 = parts2[i]
val numericPart1 = numericParts1[i]
val numericPart2 = numericParts2[i]
when {
part1 == part2 -> continue
numericPart1 != null && numericPart2 == null -> return 1
numericPart2 != null && numericPart1 == null -> return -1
numericPart1 != null && numericPart2 != null -> {
val result = numericPart1.compareTo(numericPart2)
if (result == 0) continue
return result
}
else -> {
// both are strings, we compare them taking into account special meaning
val sm1 = SPECIAL_MEANINGS[part1.lowercase()]
val sm2 = SPECIAL_MEANINGS[part2.lowercase()]
if (sm1 != null) return sm1 - (sm2 ?: 0)
if (sm2 != null) return -sm2
return part1.compareTo(part2)
}
}
}
if (lastIndex < parts1.size) {
return if (numericParts1[lastIndex] == null) -1 else 1
}
if (lastIndex < parts2.size) {
return if (numericParts2[lastIndex] == null) 1 else -1
}
return 0
}
}
}