@file:Suppress("SpellCheckingInspection") private val modules = listOf( "jieba-analysis", "apk-signer", "apk-parser", "color-picker", "material-dialogs", "material-date-time-picker", ) private val libs = listOf( "android-job-simplified-1.4.3", "androidx.appcompat-1.0.2", "apk-parser-1.0.2", "com.tencent.bugly.crashreport-4.0.4", "org.opencv-4.8.0", "paddleocr", "rapidocr", "jackpal.androidterm-1.0.70", "jackpal.androidterm.emulatorview-1.0.42", "jackpal.androidterm.libtermexec-1.0", "android-spackle-9.0.0", "android-assertion-9.0.0", "android-plugin-client-sdk-for-locale-9.0.0", "markwon-core-4.6.2", "markwon-syntax-highlight-4.6.2", ) include( ":app", *modules.map { ":modules:$it" }.toTypedArray(), *libs.map { ":libs:$it" }.toTypedArray(), ) modules.forEach { project(":modules:$it").projectDir = File("modules", it) } pluginManagement { // @Hint by SuperMonster003 on May 13, 2025. // ! Change the null value to specify a version (instead of auto-selection), e.g. 23 or "8.3.0-beta02". // ! zh-CN: 如需指定版本 (而非自动选择版本), 可修改相应的 null 值, 如 23 或 "8.3.0-beta02". // ! // ! Gradle JVM val overriddenJavaVersion: Int? = null // ! classpath("com.android.tools.build:gradle") val overriddenAgpVersion: String? = null // ! classpath("org.jetbrains.kotlin:kotlin-gradle-plugin") val overriddenKotlinVersion: String? = null // ! plugin("com.google.devtools.ksp") val overriddenKspVersion: String? = null // @Signature Pair // @Reference gradle--src.zip\gradle\dependency-management\kotlin-version.properties // @Reference https://docs.gradle.org/current/userguide/compatibility.html#kotlin // @Updated by SuperMonster003 on May 12, 2025. val embeddedKotlin: List> = listOf( /* Unofficial. */ "9.0" to "2.1.20", "8.14" to "2.1.10", "8.13" to "2.1.10", /* Official. */ "8.12" to "2.0.21", "8.11" to "2.0.20", "8.10" to "1.9.24", "8.9" to "1.9.23", "8.7" to "1.9.22", "8.5" to "1.9.20", "8.4" to "1.9.10", "8.3" to "1.9.0", "8.2" to "1.8.20", "8.0" to "1.8.10", ) // @Signature Pair // @Reference https://docs.gradle.org/current/userguide/compatibility.html#java_runtime // @Updated by SuperMonster003 on May 12, 2025. val javaGradleCompatibility = listOf( /* Unofficial. */ 25 to "9.0", /* Official. */ 24 to "8.14", 23 to "8.10", 22 to "8.8", 21 to "8.5", 20 to "8.3", 19 to "7.6", 18 to "7.5", 17 to "7.3", ) // @Signature Pair // @Reference https://developer.android.com/build/releases/gradle-plugin#updating-gradle // @Updated by SuperMonster003 on May 13, 2025. val agpGradleCompatibility: List> = listOf( "8.9" to "8.11.1", "8.8" to "8.10.2", "8.7" to "8.9", "8.6" to "8.7", "8.5" to "8.7", "8.4" to "8.6", "8.3" to "8.4", "8.2" to "8.2", "8.1" to "8.0", "8.0" to "8.0", "7.4" to "7.5", "7.3" to "7.4", ) // @Reference https://developer.android.com/reference/tools/gradle-api // @Updated by SuperMonster003 on May 13, 2025. val agpReleases: List = listOf( "8.11.0-alpha10", "8.10.0", "8.9.2", "8.8.2", "8.7.3", "8.6.1", "8.5.2", "8.4.2", "8.3.2", "8.2.2", "8.1.4", "8.0.2", "7.4.2", ) val consts = object { val DEFAULT_VERSION = "0" } val systemProperties = object { val version: String? = System.getProperty("idea.version") val platform: String? = System.getProperty("idea.paths.selector") ?: System.getProperty("idea.platform.prefix") ?: System.getProperty("java.vendor.version") val vendorName: String? = System.getProperty("idea.vendor.name") ?: System.getProperty("java.vendor") ?: System.getProperty("java.vm.vendor") val concerns: List by lazy { val concernedKeyWords = setOf("name", "vendor", "version", "platform", "paths", "os") val unconcernedKeyWords = setOf("url", "user", "runtime", "specification", "date") val unconcernedKeys = setOf( "java.class.version", "java.vm.name", "java.vm.version", "java.version", "platform.random.idempotence.check.rate", "sun.os.patch.level", ) System.getProperties().filterKeys { key -> return@filterKeys key is String && key !in unconcernedKeys && key.split(Regex("\\W")).any { it in concernedKeyWords } && key.split(Regex("\\W")).none { it in unconcernedKeyWords } }.map { (key, value) -> "[ $key: $value ]" } } var isConcernsAlreadyPrinted = false } data class Classpath(val id: String, val version: String) data class Plugin(val id: String, val version: String, val isApply: Boolean = false) class Formatted(title: String, private val contents: Collection = emptyList(), subtitle: String? = null) { private val formattedOutput = run { val elements = mutableListOf() subtitle?.let { elements.add(it) } elements.addAll(contents) val maxLength = elements.plus(title).maxOf { it.length } listOfNotNull( "=".repeat(maxLength), title, subtitle, "-".repeat(maxLength).takeUnless { contents.isEmpty() }, *contents.toTypedArray(), "=".repeat(maxLength), "", ) } fun print(contentsMatters: Boolean = false) = formattedOutput.forEach { if (!contentsMatters || contents.isNotEmpty()) println(it) } fun throwException(): Unit = throw Exception(formattedOutput.joinToString("\n")) } val config = object { /* Print concerned info by `System.getProperties()`. */ val isShowConcernedSystemProperties = true val isCleanupPaddleOcr = false val isCleanupRapidOcr = false val fallbackAgpVersion = "7.4.2" val fallbackKotlinVersion = "1.7.10" @Suppress("unused") val platforms = object { val androidStudio = object : Platform( name = "AndroidStudio", vendor = "Google", // @Reference https://developer.android.com/studio/releases#android_gradle_plugin_and_android_studio_compatibility // @Updated by SuperMonster003 on May 13, 2025. agpVersionMap = mapOf( "2025.1" to "8.10.0", /* May 13, 2025. */ "2024.3" to "8.9.2", /* Apr 11, 2025. */ "2024.2" to "8.7.3", /* Jan 13, 2025. */ "2024.1" to "8.5.2", /* Aug 30, 2024. */ "2023.3" to "8.4.2", /* Mar 28, 2024. */ "2023.2" to "8.3.2", /* Feb 14, 2024. */ "2023.1" to "8.2.2", /* Feb 6, 2024. */ "2022.3" to "8.1.4", /* Mar 31, 2024. */ "2022.2" to "8.0.2", /* May 26, 2023. */ "2022.1" to "7.4.2", /* Mar 25, 2023. */ ), // @Updated by SuperMonster003 on May 13, 2025. kotlinVersionMap = mapOf( "2024.3" to "2.1.20", /* Apr 11, 2024. */ "2024.2" to "2.1.0", /* Nov 29, 2024. */ "2024.1" to "2.0.0", /* Aug 13, 2024. */ ), codenameVersionMap = mapOf( "2025.1" to "N", /* Apr 11, 2025. */ "2024.3" to "M", /* Nov 29, 2024. */ "2024.2" to "L", /* Aug 13, 2024. */ "2024.1" to "K|L", /* May 14, 2024. */ "2023.3" to "J|K", /* Jan 21, 2024. */ "2023.2" to "I", /* Aug 25, 2023. */ "2023.1" to "H", /* May 13, 2023. */ "2022.3" to "G", /* May 3, 2023. */ "2022.2" to "F", /* May 3, 2023. */ "2022.1" to "E", /* May 3, 2023. */ ), codenameMap = mapOf( "N" to "Narwhal", /* Born on Mar 19, 2025. */ "M" to "Meerkat", /* Born on Nov 12, 2024. */ "L" to "Ladybug", /* Born on Jul 15, 2024. */ "K" to "Koala", /* Born on Mar 19, 2024. */ "J" to "Jellyfish", /* Born on Dec 28, 2023. */ "I" to "Iguana", /* Born on Aug 25, 2023. */ "H" to "Hedgehog", /* Born on Apr 25, 2023. */ "G" to "Giraffe", /* Born on Jan 17, 2023. */ "F" to "Flamingo", /* Born on Sep 20, 2022. */ "E" to "Electric Eel", /* Born on May 11, 2022. */ "D" to "Dolphin", /* Born on Jan 31, 2022. */ "C" to "Chipmunk", /* Born on Oct 13, 2021. */ "B" to "Bumblebee", /* Born on May 18, 2021. */ "A" to "Arctic Fox", /* Born on Jan 26, 2021. */ // @Hint by SuperMonster003 on Oct 18, 2023. // ! The codenames below were predicted but not official. "O" to "Ostrich", "P" to "Penguin", "Q" to "Quail", "R" to "Rhino", "S" to "Snail", "T" to "Tiger", "U" to "Unicorn", "V" to "Vicuna", "W" to "Walrus", "X" to "Xiphias", "Y" to "Yeti", "Z" to "Zebra", ), ) { override val weight = Int.MAX_VALUE override val gradleSettingsName = "Gradle JDK" override val fullName by lazy { val suffix = codenameVersionMap?.get(version) ?.split("|") ?.joinToString(" / ", prefix = " ") { key -> codenameMap?.get(key.trim()) ?: key } ?: "" return@lazy "Android Studio$suffix" } } val intelliJIdea = object : Platform( name = "IntelliJIdea", vendor = "Jetbrains", // @Updated by SuperMonster003 on Apr 23, 2025. agpVersionMap = mapOf( "2025.1" to "8.9.2", /* Apr 23, 2025. */ "2024.3.1" to "8.7.3", /* Dec 10, 2024. */ "2024.3" to "8.7.0-rc01", /* Nov 15, 2024. */ "2024.2" to "8.5.2", /* Aug 13, 2024. */ "2024.1" to "8.2.2", /* Apr 6, 2024. */ "2023.3" to "8.2.2", /* Jan 19, 2024. */ "2023.1" to "7.4.2", /* May 26, 2023. */ "2022.3" to "7.4.0-beta02", /* Mar 25, 2023. */ ), // @Updated by SuperMonster003 on Apr 23, 2025. kotlinVersionMap = mapOf( "2025.1" to "2.1.20", /* Apr 12, 2025. */ "2024.3.4" to "2.1.10", /* Feb 28, 2025. */ "2024.2.3" to "2.0.21", /* Oct 17, 2024. */ "2024.2" to "2.0.21-RC", /* Sep 27, 2024. */ "2024.1" to "1.9.24", /* Dec 3, 2024. */ "2023.3" to "1.9.23", /* Mar 29, 2024. */ ), ) { override val weight = 10 override val gradleSettingsName = "Gradle JVM" override val fullName = "IntelliJ IDEA" } val temurin = object : Platform( name = "Temurin", vendor = "temurin", /* More common as "Eclipse Adoptium". */ agpVersionMap = mapOf( "21.0.6+7" to "8.7.3", /* Apr 16, 2025. */ "20.0.2+9" to "8.2.2", /* Dec 2, 2024. */ ), kotlinVersionMap = mapOf( "21.0.6+7" to "2.1.10", /* Apr 16, 2025. */ "20.0.2+9" to "1.9.24", /* Dec 2, 2024. */ ), ) { override val weight = 5 override val shouldPrintProgress = false } val unknown = object : Platform( name = "Unknown", vendor = "unknown", ) { fun declare() = systemProperties.platform?.let { println("Unexpected platform: $it") } ?: Formatted( "Current platform is unknown", systemProperties.concerns, "However, here are some props may be useful for determining platform info", ).print().also { systemProperties.isConcernsAlreadyPrinted = true } } fun determine(): Platform { val candidates = this::class.java.declaredFields.mapNotNull { field -> field.get(this)?.let { tmpPlatform -> tmpPlatform::class.java.methods.find { method -> method.name == Platform::matchEnvironment.name && method.invoke(tmpPlatform) == true }?.let { tmpPlatform as? Platform } } } return when { candidates.isEmpty() -> when (val osName = System.getProperty("os.name")) { is String -> unknown.also { it.name = osName } else -> unknown.also { it.declare() } } candidates.size > 1 -> candidates.maxBy { it.weight } else -> candidates.first() }.also { it.version = systemProperties.version ?: when { it != unknown && systemProperties.platform != null -> { systemProperties.platform.substring(it.name.length) .replace(Regex("^\\W*"), "") .replace(Regex("^Preview", RegexOption.IGNORE_CASE), "") } else -> consts.DEFAULT_VERSION } } } } val libs = listOf( Classpath(id = "com.android.tools.build:gradle", version = overriddenAgpVersion ?: "auto:agp"), Classpath(id = "org.jetbrains.kotlin:kotlin-gradle-plugin", version = overriddenKotlinVersion ?: "auto:kotlin"), Plugin(id = "com.google.devtools.ksp", version = overriddenKspVersion ?: "auto:ksp"), ) // @Reference https://github.com/google/ksp/releases // @Updated by SuperMonster003 on May 13, 2025. val kspVersionMap = mapOf( "2.2.0-Beta2" to "2.0.1", /* May 2, 2025. */ "2.2.0-Beta1" to "2.0.0", /* Apr 17, 2025. */ "2.1.21-RC2" to "2.0.1", /* May 2, 2025. */ "2.1.21-RC" to "2.0.0", /* Apr 18, 2025. */ "2.1.20" to "2.0.1", /* May 1, 2025. */ "2.1.20-RC3" to "1.0.31", /* Mar 15, 2025. */ "2.1.20-RC2" to "1.0.31", /* Mar 7, 2025. */ "2.1.20-RC" to "1.0.31", /* Feb 28, 2025. */ "2.1.20-Beta2" to "1.0.30", /* Feb 15, 2025. */ "2.1.20-Beta1" to "1.0.29", /* Dec 25, 2024. */ "2.1.10" to "1.0.31", /* Feb 28, 2025. */ "2.1.10-RC" to "1.0.29", /* Jan 10, 2025. */ "2.1.0" to "1.0.29", /* Nov 28, 2024. */ "2.1.0-RC2" to "1.0.29", /* Jan 22, 2025. */ "2.1.0-RC" to "1.0.27", /* Nov 8, 2024. */ "2.1.0-Beta2" to "1.0.26", /* Oct 26, 2024. */ "2.1.0-Beta1" to "1.0.25", /* Sep 25, 2024. */ "2.0.21" to "1.0.28", /* Nov 16, 2024. */ "2.0.21-RC" to "1.0.25", /* Oct 4, 2024. */ "2.0.20" to "1.0.25", /* Sep 7, 2024. */ "2.0.20-RC2" to "1.0.24", /* Aug 24, 2024. */ "2.0.20-RC" to "1.0.24", /* Aug 8, 2024. */ "2.0.20-Beta2" to "1.0.23", /* Aug 8, 2024. */ "2.0.20-Beta1" to "1.0.22", /* Aug 8, 2024. */ "2.0.10" to "1.0.24", /* Aug 8, 2024. */ "2.0.10-RC2" to "1.0.24", /* Aug 8, 2024. */ "2.0.10-RC" to "1.0.23", /* Aug 8, 2024. */ "2.0.0" to "1.0.24", /* Aug 8, 2024. */ "2.0.0-RC3" to "1.0.20", /* May 24, 2024. */ "2.0.0-RC2" to "1.0.20", /* May 24, 2024. */ "2.0.0-RC1" to "1.0.20", /* May 24, 2024. */ "2.0.0-Beta5" to "1.0.20", /* May 24, 2024. */ "2.0.0-Beta4" to "1.0.19", /* May 24, 2024. */ "2.0.0-Beta3" to "1.0.17", /* May 24, 2024. */ "2.0.0-Beta2" to "1.0.16", /* May 24, 2024. */ "2.0.0-Beta1" to "1.0.15", /* May 24, 2024. */ "1.9.25" to "1.0.20", /* Aug 8, 2024. */ "1.9.24" to "1.0.20", /* May 14, 2024. */ "1.9.23" to "1.0.20", /* Apr 6, 2024. */ "1.9.22" to "1.0.17", /* Jan 20, 2024. */ "1.9.21" to "1.0.15", /* Dec 2, 2023. */ "1.9.20" to "1.0.14", /* Nov 9, 2023. */ "1.9.20-RC2" to "1.0.13", /* Oct 26, 2023. */ "1.9.20-RC" to "1.0.13", /* Oct 12, 2023. */ "1.9.20-Beta2" to "1.0.13", /* Sep 29, 2023. */ "1.9.20-Beta" to "1.0.13", /* Sep 12, 2023. */ "1.9.10" to "1.0.13", /* Sep 11, 2023. */ "1.9.0" to "1.0.13", /* Aug 16, 2023. */ "1.9.0-RC" to "1.0.11", /* Aug 16, 2023. */ "1.8.21" to "1.0.11", /* Aug 16, 2023. */ "1.8.20-RC2" to "1.0.9", /* Aug 16, 2023. */ "1.8.0" to "1.0.9", /* Aug 16, 2023. */ "1.8.0-RC2" to "1.0.8", /* Aug 16, 2023. */ ) abstract inner class Platform( var name: String, val vendor: String, val agpVersionMap: Map = emptyMap(), val kotlinVersionMap: Map = emptyMap(), val codenameVersionMap: Map? = null, val codenameMap: Map? = null, ) { open val gradleSettingsName: String? = null open val weight: Int = -Int.MAX_VALUE open var version: String = consts.DEFAULT_VERSION @Suppress("unused") open val shouldPrintProgress: Boolean = true open val fullName get() = uppercaseFirstChar(name) open fun matchEnvironment() = systemProperties.platform?.startsWith(name) == true || systemProperties.vendorName?.contains(vendor, true) == true fun ensureMinimalGradleJdkVersion() { val minVer = java.util.Properties().apply { load(java.io.FileInputStream("$rootDir/version.properties")) }["JAVA_VERSION_MIN_SUPPORTED"].let { it as String }.toInt() if (JavaVersion.current().majorVersion.toInt() < minVer) { Formatted( "Current Gradle JDK version ${JavaVersion.current()} does not meet " + "the minimum requirement which $minVer is needed", mutableListOf().apply { gradleSettingsName?.let { add("Settings path: File | Settings | Build, Execution, Deployment | Build Tools | Gradle") } add("Change \"${gradleSettingsName ?: "Gradle JDK"}\" to $minVer at the least") } ).throwException() } } fun prependConsoleInformation(consoleInfo: MutableList) { val versionSuffix = when (this.version.isNotEmpty() && this.version != consts.DEFAULT_VERSION) { true -> " | ${this.version}" else -> "" } consoleInfo.add(0, "Platform: ${this.fullName}$versionSuffix") } private fun uppercaseFirstChar(s: String) = when (s.isEmpty()) { true -> "" else -> s[0].uppercase() + s.substring(1) } } } val identifier = object { // @Hint by SuperMonster003 on Oct 18, 2024. // ! Fallback is a temporary or alternative solution // ! used to handle some specific situations where functionality is unavailable. // ! Compatibility is a broader design principle which ensures new versions // ! remain compatible and continue to support the operations and data of older versions. // ! zh-CN: // ! Fallback (回退) 是一种临时或替代的解决方法, 用于应对功能不可用的特定情况. // ! Compatibility (兼容) 是一种更广泛的设计原则, 确保新版本能兼容并继续支持旧版本的操作和数据. val fallback = "fallback" val upgraded = "upgraded" val downgraded = "downgraded" val nearestLowerMatched = "nearest-lower-matched" val specified = "specified" val auto = "auto" val fallbackSuffix = " [$fallback]" val upgradedSuffix = " [$upgraded]" val downgradedSuffix = " [$downgraded]" val nearestLowerMatchedSuffix = " [$nearestLowerMatched]" val autoSpecifiedSuffix = " [$auto-$specified]" val userSpecifiedSuffix = " [user-$specified]" } val platform = config.platforms.determine() val console = object { var versionInfo = mutableListOf() fun printConcernedSystemPropertiesIfNeeded() { if (config.isShowConcernedSystemProperties && !systemProperties.isConcernsAlreadyPrinted) { Formatted("Information for concerned system properties", systemProperties.concerns).print(true) } } fun printVersionsOfIdeAndGradlePlugins() { Formatted("Version information for IDE platform and Gradle plugins", versionInfo).print() } } platform.ensureMinimalGradleJdkVersion() platform.prependConsoleInformation(console.versionInfo) val notations = object { private val versions = object { val agp = object : Version(platform.agpVersionMap, platform.version, config.fallbackAgpVersion) { override fun refinedBestMatchingValue(bestMatchingValue: String?): String? { val currentGradleVersion = gradle.gradleVersion.toGradleVersion() val sorted = agpGradleCompatibility.sortedByDescending { it.second.toGradleVersion() } var maxSupporedAgpVersionPrefix: String? = null for (pair in sorted) { maxSupporedAgpVersionPrefix = pair.first if (currentGradleVersion >= pair.second.toGradleVersion()) { break } } val maxSupporedAgpVersion = maxSupporedAgpVersionPrefix?.let { getAgpReleasedVersion(it) } return when { maxSupporedAgpVersion == null -> bestMatchingValue bestMatchingValue == null -> { bestMatchingOperationHintSuffix = identifier.autoSpecifiedSuffix maxSupporedAgpVersion } compareVersionStrings(bestMatchingValue, maxSupporedAgpVersion) > 0 -> { bestMatchingOperationHintSuffix = identifier.downgradedSuffix maxSupporedAgpVersion } else -> bestMatchingValue } } private fun getAgpReleasedVersion(referenceAgpVersion: String): String? { return agpReleases.find { it.startsWith(referenceAgpVersion) && !it.contains("-") } } } val kotlin = object : Version(platform.kotlinVersionMap, platform.version, config.fallbackKotlinVersion) { override fun refinedBestMatchingValue(bestMatchingValue: String?): String? { val currentGradleVersion = gradle.gradleVersion.toGradleVersion() val embeddedMin = embeddedKotlin .filter { (gradleMin, _) -> currentGradleVersion >= gradleMin.toGradleVersion() } .maxByOrNull { it.first.toGradleVersion() } ?.second return when { embeddedMin == null -> bestMatchingValue bestMatchingValue == null -> { bestMatchingOperationHintSuffix = identifier.autoSpecifiedSuffix embeddedMin } bestMatchingValue.toGradleVersion() < embeddedMin.toGradleVersion() -> { bestMatchingOperationHintSuffix = identifier.upgradedSuffix embeddedMin } bestMatchingValue.toGradleVersion() > embeddedMin.toGradleVersion() -> { bestMatchingOperationHintSuffix = identifier.downgradedSuffix embeddedMin } else -> bestMatchingValue } } } } private val pluginVersionAutomatorMap: Map) -> Map> = mapOf( "ksp" to { versionMap: Map -> var suffix = "" val kotlinVersion = config.libs.filterIsInstance() .find { it.id.contains("kotlin-gradle-plugin", true) } ?.takeIf { !it.version.startsWith("${identifier.auto}:") } ?.version?.let { kt -> versionMap[kt]?.let { kt } } ?: versions.kotlin.bestMatchingValue?.also { suffix += identifier.autoSpecifiedSuffix } ?: versions.kotlin.fallbackVersion?.let { kt -> suffix += identifier.fallbackSuffix versionMap[kt]?.let { kt } } val kspVersion = kotlinVersion?.let { kt -> val ver = Version(config.kspVersionMap, kt) val key = ver.bestMatchingKey ?: return@let null val value = ver.bestMatchingValue ?: return@let null ver.bestMatchingOperationHintSuffix?.let { suffix += it } "$key-$value" } mapOf("version" to kspVersion, "suffix" to suffix) }, ) val classpath = config.libs.filterIsInstance().map { lib -> var suffix = "" val version: String = when { lib.version.startsWith("${identifier.auto}:") -> { val mapType = lib.version.substring("${identifier.auto}:".length) val ver = when (mapType) { "agp" -> versions.agp "kotlin" -> versions.kotlin else -> throw Exception("Unknown version ${lib.version} for classpath ${lib.id}") } ver.bestMatchingValue?.also { ver.bestMatchingOperationHintSuffix?.let { s -> suffix += s } } ?: ver.fallbackVersion?.also { suffix += identifier.fallbackSuffix } ?: consts.DEFAULT_VERSION } else -> lib.version.also { suffix += identifier.userSpecifiedSuffix } } if (lib.id == "com.android.tools.build:gradle") { gradle.extra.set("javaVersionCoercedByGradle", getMaxSupportedJavaVersion(version)) gradle.extra.set("javaVersionOverriddenByUser", overriddenJavaVersion) } "${lib.id}:$version".also { notation -> console.versionInfo += "Classpath: \"$notation\"$suffix" } } val plugins = config.libs.filterIsInstance().map { lib -> var suffix = "" val version: String = when { lib.version.startsWith("${identifier.auto}:") -> { val automator = pluginVersionAutomatorMap[lib.version.substring("${identifier.auto}:".length)]!! val result = automator.invoke(config.kspVersionMap) result["suffix"]?.let { s -> suffix += s } result["version"] ?: throw Exception("Unknown version for plugin ${lib.id}") } else -> { suffix += identifier.userSpecifiedSuffix lib.version } } console.versionInfo += "Plugin: \"${lib.id}:$version\"$suffix" mapOf("id" to lib.id, "version" to version, "isApply" to lib.isApply) } private open inner class Version(val map: Map, platformVersion: String, val fallbackVersion: String? = null) { val bestMatchingKey: String? = findBestMatchingMapKey(platformVersion) var bestMatchingOperationHintSuffix: String? = identifier.nearestLowerMatchedSuffix.takeIf { bestMatchingKey != null && bestMatchingKey != platformVersion } val bestMatchingValue: String? = refinedBestMatchingValue(bestMatchingKey?.let { map[it] }) open fun refinedBestMatchingValue(bestMatchingValue: String?) = bestMatchingValue fun findBestMatchingMapKey(platformVersion: String): String? { val (platformVersionNumbers, platformVersionSuffix) = toVersionParts(platformVersion) val sortedVersions = map.keys.filter { it != identifier.fallback }.sortedWith(::compareVersionStrings).reversed() for (version in sortedVersions) { val (versionNumbers, versionSuffix) = toVersionParts(version) val versionComparisonScore = compareVersionParts(versionNumbers, platformVersionNumbers) if (versionComparisonScore < 0) { return version } if (versionComparisonScore == 0 && compareSuffix(versionSuffix, platformVersionSuffix) <= 0) { return version } } return null } fun compareVersionStrings(v1: String, v2: String): Int { val (ver1Numbers, ver1Suffix) = toVersionParts(v1) val (ver2Numbers, ver2Suffix) = toVersionParts(v2) return compareVersionParts(ver1Numbers, ver2Numbers) .takeIf { it != 0 } ?: compareSuffix(ver1Suffix, ver2Suffix) } private fun compareVersionParts(parts1: List, parts2: List): Int { for (i in 0 until maxOf(parts1.size, parts2.size)) { val part1 = parts1.getOrElse(i) { 0 } val part2 = parts2.getOrElse(i) { 0 } if (part1 != part2) return part1.compareTo(part2) } return 0 } private fun compareSuffix(suffix1: Pair, suffix2: Pair): Int { val suffixPriority = mapOf("" to 10, "Alpha" to 1, "Beta" to 2, "RC" to 5) val (suffixName1, suffixNumber1) = suffix1 val (suffixName2, suffixNumber2) = suffix2 val priority1 = suffixPriority[suffixName1] ?: Int.MAX_VALUE val priority2 = suffixPriority[suffixName2] ?: Int.MAX_VALUE return priority1.compareTo(priority2).takeIf { it != 0 } ?: suffixNumber1.compareTo(suffixNumber2) } private fun toVersionParts(version: String): Pair, Pair> { val parts = version.split(Regex("[+-]")) val numberParts = parts[0].split('.').map { it.toIntOrNull() ?: throw IllegalArgumentException("Invalid version part: '$it' in version: '$version'") } val suffixPattern = Regex("([A-Za-z]+)(\\d*)|([A-Za-z]*)(\\d+)") val suffixMatch = suffixPattern.matchEntire(parts.getOrElse(1) { "" }) ?: return numberParts to ("" to 0) val suffixName = suffixMatch.groupValues[1] // "Alpha", "Beta", "RC" or empty string val suffixNumber = suffixMatch.groupValues[2].toIntOrNull() ?: 1 // Default to 1 for suffixes like "Alpha", "Beta", "RC" return numberParts to (suffixName to suffixNumber) } } private fun String.toGradleVersion() = GradleVersion.version(this) private fun getMaxSupportedJavaVersion(gradleVersion: String): Int { fun parseVersion(version: String) = version.split(Regex("[.-]")).map { it.toIntOrNull() ?: 0 } val inputGradleVersionInts = parseVersion(gradleVersion) val sortedJavaGradleCompatibility = javaGradleCompatibility.sortedBy { it.first } var maxJavaVersion: Int = sortedJavaGradleCompatibility.first().first for ((presetJavaVersion, presetGradleVersion) in sortedJavaGradleCompatibility) { val presetGradleVersionInts: List = parseVersion(presetGradleVersion) for (i in presetGradleVersionInts.indices) { when { i > inputGradleVersionInts.lastIndex -> { break } inputGradleVersionInts[i] > presetGradleVersionInts[i] -> { maxJavaVersion = presetJavaVersion break } inputGradleVersionInts[i] < presetGradleVersionInts[i] -> { break } i == presetGradleVersionInts.lastIndex -> { maxJavaVersion = presetJavaVersion } } } } return maxJavaVersion } } buildscript { repositories { mavenCentral() google() } dependencies /* Android/Kotlin Gradle Plugin. */ { notations.classpath.forEach { classpath(it) } } dependencies /* Apache Compress for utils.build.gradle module. */ { classpath("org.apache.commons:commons-compress:1.27.1") classpath("org.tukaani:xz:1.9") } } plugins { notations.plugins.forEach { id(it["id"] as String) version it["version"] as String apply it["isApply"] as Boolean } } gradle.taskGraph.whenReady { if (allTasks.none { it.name == "clean" }) { console.printConcernedSystemPropertiesIfNeeded() console.printVersionsOfIdeAndGradlePlugins() } } gradle.extra.apply { set("isCleanupPaddleOcr", config.isCleanupPaddleOcr) set("isCleanupRapidOcr", config.isCleanupRapidOcr) } gradle.beforeProject { extensions.extraProperties["kotlinVersion"] = notations.classpath.find { it.contains("kotlin-gradle-plugin") }?.substringAfterLast(":") ?: config.fallbackKotlinVersion extensions.extraProperties["platform"] = platform } }