Fix version parsing crash on Gradle rich version string
Gradle accepts different types of version string declaration: https://docs.gradle.org/current/userguide/single_versions.html. Now code should parse such declarations into semantic versioning without exception. ^KT-55255 Fixed
This commit is contained in:
committed by
Space Team
parent
25553b81d5
commit
ff73e7a37a
+1
-1
@@ -43,7 +43,7 @@ private val Dependency.isKotlinTestRootDependency: Boolean
|
||||
|
||||
private val kotlin150Version = SemVer(1.toBigInteger(), 5.toBigInteger(), 0.toBigInteger())
|
||||
|
||||
private fun isAtLeast1_5(version: String) = SemVer.from(version) >= kotlin150Version
|
||||
private fun isAtLeast1_5(version: String) = SemVer.fromGradleRichVersion(version) >= kotlin150Version
|
||||
|
||||
private val jvmPlatforms = setOf(KotlinPlatformType.jvm, KotlinPlatformType.androidJvm)
|
||||
|
||||
|
||||
+1
-1
@@ -62,7 +62,7 @@ internal fun ConfigurationContainer.configureStdlibVersionAlignment() = all { co
|
||||
if (dependency.group == KOTLIN_MODULE_GROUP &&
|
||||
(dependency.name == "kotlin-stdlib" || dependency.name == "kotlin-stdlib-jdk7") &&
|
||||
dependency.version != null &&
|
||||
SemVer.from(dependency.version!!) >= kotlin180Version
|
||||
SemVer.fromGradleRichVersion(dependency.version!!) >= kotlin180Version
|
||||
) {
|
||||
if (configuration.isCanBeResolved) configuration.alignStdlibJvmVariantVersions(dependency)
|
||||
|
||||
|
||||
+89
-1
@@ -83,6 +83,94 @@ data class SemVer(
|
||||
build.takeIf { it.isNotBlank() }
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses Gradle [rich versions](https://docs.gradle.org/current/userguide/single_versions.html) version string.
|
||||
* In case of ranges, version prefixes or latest status - returned version will be closest using [Int.MAX_VALUE] as highest possible
|
||||
* version number for major, minor or patch.
|
||||
*/
|
||||
fun fromGradleRichVersion(version: String): SemVer {
|
||||
return when {
|
||||
version == "+" || version.startsWith("latest.") ->
|
||||
SemVer(Int.MAX_VALUE.toBigInteger(), Int.MAX_VALUE.toBigInteger(), Int.MAX_VALUE.toBigInteger())
|
||||
version.matches(MAJOR_PREFIX_VERSION) ->
|
||||
from("${version.replaceFirst("+", Int.MAX_VALUE.toString())}.${Int.MAX_VALUE}", loose = true)
|
||||
version.matches(MINOR_PREFIX_VERSION) ->
|
||||
from(version.replaceFirst("+", Int.MAX_VALUE.toString()), loose = true)
|
||||
version.matches(FINITE_RANGE) -> {
|
||||
if (version.endsWith(CLOSE_INC)) {
|
||||
from(FINITE_RANGE.find(version)!!.groups[2]!!.value, loose = true)
|
||||
} else {
|
||||
from(FINITE_RANGE.find(version)!!.groups[2]!!.value, loose = true).decrement()
|
||||
}
|
||||
}
|
||||
version.matches(LOWER_INFINITE_RANGE) -> {
|
||||
if (version.endsWith(CLOSE_INC)) {
|
||||
from(LOWER_INFINITE_RANGE.find(version)!!.groups[1]!!.value, loose = true)
|
||||
} else {
|
||||
from(LOWER_INFINITE_RANGE.find(version)!!.groups[1]!!.value, loose = true).decrement()
|
||||
}
|
||||
}
|
||||
version.matches(UPPER_INFINITE_RANGE) -> {
|
||||
SemVer(Int.MAX_VALUE.toBigInteger(), Int.MAX_VALUE.toBigInteger(), Int.MAX_VALUE.toBigInteger())
|
||||
}
|
||||
version.matches(SINGLE_VALUE_RANGE) -> {
|
||||
from(SINGLE_VALUE_RANGE.find(version)!!.groups[1]!!.value, loose = true)
|
||||
}
|
||||
else -> from(version, loose = true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun SemVer.decrement(): SemVer {
|
||||
return if (patch == 0.toBigInteger()) {
|
||||
if (minor == 0.toBigInteger()) {
|
||||
SemVer(major.dec(), Int.MAX_VALUE.toBigInteger(), Int.MAX_VALUE.toBigInteger())
|
||||
} else {
|
||||
SemVer(major, minor.dec(), Int.MAX_VALUE.toBigInteger())
|
||||
}
|
||||
} else {
|
||||
SemVer(major, minor, patch.dec())
|
||||
}
|
||||
}
|
||||
|
||||
private val MAJOR_PREFIX_VERSION = "^[0-9]+\\.\\+$".toRegex()
|
||||
private val MINOR_PREFIX_VERSION = "^[0-9]+\\.[0-9]+\\.\\+$".toRegex()
|
||||
|
||||
// Following constants and logic around was peeked from
|
||||
// https://github.com/gradle/gradle/blob/master/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionRangeSelector.java
|
||||
private const val OPEN_INC = "["
|
||||
private const val OPEN_EXC = "]"
|
||||
private const val OPEN_EXC_MAVEN = "("
|
||||
private const val CLOSE_INC = "]"
|
||||
private const val CLOSE_EXC = "["
|
||||
private const val CLOSE_EXC_MAVEN = ")"
|
||||
private const val LOWER_INFINITE = "("
|
||||
private const val UPPER_INFINITE = ")"
|
||||
private const val SEPARATOR = ","
|
||||
|
||||
private const val OPEN_INC_PATTERN = "\\" + OPEN_INC
|
||||
private const val OPEN_EXC_PATTERN = "\\" + OPEN_EXC + "\\" + OPEN_EXC_MAVEN
|
||||
private const val CLOSE_INC_PATTERN = "\\" + CLOSE_INC
|
||||
private const val CLOSE_EXC_PATTERN = "\\" + CLOSE_EXC + "\\" + CLOSE_EXC_MAVEN
|
||||
private const val LI_PATTERN = "\\" + LOWER_INFINITE
|
||||
private const val UI_PATTERN = "\\" + UPPER_INFINITE
|
||||
private const val SEP_PATTERN = "\\s*\\$SEPARATOR\\s*"
|
||||
private const val OPEN_PATTERN = "[$OPEN_INC_PATTERN$OPEN_EXC_PATTERN]"
|
||||
private const val CLOSE_PATTERN = "[$CLOSE_INC_PATTERN$CLOSE_EXC_PATTERN]"
|
||||
private const val ANY_NON_SPECIAL_PATTERN = ("[^\\s" + SEPARATOR + OPEN_INC_PATTERN
|
||||
+ OPEN_EXC_PATTERN + CLOSE_INC_PATTERN + CLOSE_EXC_PATTERN + LI_PATTERN + UI_PATTERN
|
||||
+ "]")
|
||||
private const val FINITE_PATTERN = (OPEN_PATTERN + "\\s*(" + ANY_NON_SPECIAL_PATTERN
|
||||
+ "+)" + SEP_PATTERN + "(" + ANY_NON_SPECIAL_PATTERN + "+)\\s*" + CLOSE_PATTERN)
|
||||
private const val LOWER_INFINITE_PATTERN = (LI_PATTERN + SEP_PATTERN + "("
|
||||
+ ANY_NON_SPECIAL_PATTERN + "+)\\s*" + CLOSE_PATTERN)
|
||||
private const val UPPER_INFINITE_PATTERN = (OPEN_PATTERN + "\\s*("
|
||||
+ ANY_NON_SPECIAL_PATTERN + "+)" + SEP_PATTERN + UI_PATTERN)
|
||||
private const val SINGLE_VALUE_PATTERN = "$OPEN_INC_PATTERN\\s*($ANY_NON_SPECIAL_PATTERN+)$CLOSE_INC_PATTERN"
|
||||
private val FINITE_RANGE = FINITE_PATTERN.toRegex()
|
||||
private val LOWER_INFINITE_RANGE = LOWER_INFINITE_PATTERN.toRegex()
|
||||
private val UPPER_INFINITE_RANGE = UPPER_INFINITE_PATTERN.toRegex()
|
||||
private val SINGLE_VALUE_RANGE = SINGLE_VALUE_PATTERN.toRegex()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,4 +283,4 @@ fun min(a: SemVer, b: SemVer): SemVer =
|
||||
if (a < b) a else b
|
||||
|
||||
fun max(a: SemVer, b: SemVer): SemVer =
|
||||
if (a > b) a else b
|
||||
if (a > b) a else b
|
||||
|
||||
+37
@@ -44,4 +44,41 @@ class SemVerTest {
|
||||
).sorted().joinToString(", ")
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testParseGradleRichVersions() {
|
||||
val maxInt = Int.MAX_VALUE.toBigInteger()
|
||||
listOf(
|
||||
// Version prefix
|
||||
SemVer(maxInt, maxInt, maxInt) to SemVer.fromGradleRichVersion("+"),
|
||||
SemVer(1.toBigInteger(), maxInt, maxInt) to SemVer.fromGradleRichVersion("1.+"),
|
||||
SemVer(1.toBigInteger(), 10.toBigInteger(), maxInt) to SemVer.fromGradleRichVersion("1.10.+"),
|
||||
SemVer(1.toBigInteger(), 10.toBigInteger(), 0.toBigInteger()) to SemVer.fromGradleRichVersion("1.10.0.+"),
|
||||
|
||||
// Version latest state
|
||||
SemVer(maxInt, maxInt, maxInt) to SemVer.fromGradleRichVersion("latest.release"),
|
||||
SemVer(maxInt, maxInt, maxInt) to SemVer.fromGradleRichVersion("latest.integration"),
|
||||
|
||||
// Ranges
|
||||
SemVer(1.toBigInteger(), 5.toBigInteger(), 0.toBigInteger()) to SemVer.fromGradleRichVersion("(1.2,1.5]"),
|
||||
SemVer(1.toBigInteger(), 5.toBigInteger(), 55.toBigInteger()) to SemVer.fromGradleRichVersion("[1.2,1.5.55]"),
|
||||
SemVer(1.toBigInteger(), 5.toBigInteger(), 54.toBigInteger()) to SemVer.fromGradleRichVersion("[1.2,1.5.55-SNAPSHOT["),
|
||||
SemVer(1.toBigInteger(), maxInt, maxInt) to SemVer.fromGradleRichVersion("[1.1,2.0)"),
|
||||
SemVer(1.toBigInteger(), maxInt, maxInt) to SemVer.fromGradleRichVersion("(1.1,2.0)"),
|
||||
SemVer(1.toBigInteger(), maxInt, maxInt) to SemVer.fromGradleRichVersion("(1.1,2.0-SNAPSHOT)"),
|
||||
SemVer(1.toBigInteger(), maxInt, maxInt) to SemVer.fromGradleRichVersion("]1.0, 2.0["),
|
||||
SemVer(1.toBigInteger(), maxInt, maxInt) to SemVer.fromGradleRichVersion("[1.0, 2.0["),
|
||||
SemVer(1.toBigInteger(), 0.toBigInteger(), 0.toBigInteger()) to SemVer.fromGradleRichVersion("(,1.0]"),
|
||||
SemVer(1.toBigInteger(), 0.toBigInteger(), 0.toBigInteger(), "SNAPSHOT") to SemVer.fromGradleRichVersion("(,1.0-SNAPSHOT]"),
|
||||
SemVer(0.toBigInteger(), maxInt, maxInt) to SemVer.fromGradleRichVersion("(,1.0)"),
|
||||
SemVer(maxInt, maxInt, maxInt) to SemVer.fromGradleRichVersion("[1.0,)"),
|
||||
SemVer(10.toBigInteger(), 0.toBigInteger(), 20.toBigInteger()) to SemVer.fromGradleRichVersion("[10.0.20]"),
|
||||
|
||||
// Normal
|
||||
SemVer(10.toBigInteger(), 0.toBigInteger(), 20.toBigInteger()) to SemVer.fromGradleRichVersion("10.0.20"),
|
||||
SemVer(10.toBigInteger(), 0.toBigInteger(), 0.toBigInteger(), "SNAPSHOT") to SemVer.fromGradleRichVersion("10.0-SNAPSHOT"),
|
||||
).forEach { (expected, actual) ->
|
||||
assertEquals(expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user