diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/kotlinTestDependencyManagement.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/kotlinTestDependencyManagement.kt index 3aff5e9f551ba..160ad22cf5c11 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/kotlinTestDependencyManagement.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/kotlinTestDependencyManagement.kt @@ -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) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/stdlibDependencyManagement.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/stdlibDependencyManagement.kt index ac84fd189837b..fcd0504d2b9e7 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/stdlibDependencyManagement.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/stdlibDependencyManagement.kt @@ -67,7 +67,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) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/semver.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/semver.kt index cee961e6edf86..529bd9cfd8b99 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/semver.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/semver.kt @@ -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 \ No newline at end of file + if (a > b) a else b diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/SemVerTest.kt b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/SemVerTest.kt index e6d8da7454156..df4b1e740b1c7 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/SemVerTest.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/SemVerTest.kt @@ -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) + } + } } \ No newline at end of file