@@ -85,29 +85,35 @@ class GenerateMatrix : CliktCommand() {
8585 throw ProgramResult (1 )
8686 }
8787
88- // TODO: for now this is manual, but we could try get it from Gradle's github in the future
89- val gradleToKotlin =
90- mapOf (
91- " 7.5 " .toVersion(strict = false ) to " 1.8.20 " ,
92- " 9.0.0 " .toVersion(strict = false ) to " 2.1.0 " ,
93- " 9.5.0-0 " .toVersion(strict = false ) to " 2.3.0 " ,
94- )
95- // TODO: make it dynamic too
96- val kotlinVersion = " 2.1.0 " .toVersion()
88+ val agpToKotlin =
89+ try {
90+ fetchAgpKotlinCompatibility()
91+ } catch (e : Exception ) {
92+ print (e.printStackTrace())
93+ echo( " Error parsing AGP Kotlin compatibility " )
94+ throw ProgramResult ( 1 )
95+ }
96+
9797 val baseIncludes = buildList {
9898 for (entry in agpToGradle.entries) {
9999 add(
100100 buildMap {
101- put(" agp" , entry.key.toString())
101+ val agpVersion = entry.key
102+ put(" agp" , agpVersion.toString())
102103 val gradle = entry.value
103104
104- // Check if the Gradle version meets Kotlin's minimum requirement
105- // Use the current Kotlin version's minimum requirement
105+ // Pick the latest Kotlin whose required AGP <= this AGP
106+ val kotlinVersion =
107+ agpToKotlin
108+ .filter { (minAgp, _) -> agpVersion >= minAgp }
109+ .maxByOrNull { it.first }
110+ ?.second
111+
112+ // Floor: if the chosen Kotlin requires a newer Gradle than AGP does, bump Gradle up
106113 val kotlinMinGradle =
107- kotlinToGradleMap.entries
108- .find { (kotlin, _) -> kotlin.inRange(kotlinVersion) }
109- ?.value
110- ?.min
114+ kotlinVersion?.let { kv ->
115+ kotlinToGradleMap.entries.find { (kotlin, _) -> kotlin.inRange(kv) }?.value?.min
116+ }
111117 val finalGradle =
112118 if (kotlinMinGradle != null && gradle < kotlinMinGradle) {
113119 echo(
@@ -127,9 +133,8 @@ class GenerateMatrix : CliktCommand() {
127133 )
128134 // TODO: if needed we can test against different Java versions
129135 put(" java" , " 17" )
130- val kotlin = gradleToKotlin.entries.findLast { finalGradle >= it.key }?.value
131- if (kotlin != null ) {
132- put(" kotlin" , kotlin)
136+ if (kotlinVersion != null ) {
137+ put(" kotlin" , kotlinVersion.toString())
133138 }
134139 }
135140 )
@@ -356,6 +361,47 @@ class GenerateMatrix : CliktCommand() {
356361 return gradleVersions to latestGradle
357362 }
358363
364+ /* *
365+ * Fetches the AGP -> Kotlin compatibility table from developer.android.com/build/kotlin-support.
366+ * Each row lists a Kotlin minor and the minimum AGP version required for it; returned as pairs
367+ * (minAGP, Kotlin) so callers can pick the highest Kotlin whose minAGP is <= a given AGP.
368+ */
369+ private fun fetchAgpKotlinCompatibility (): List <Pair <Version , Version >> {
370+ val html = URL (" https://developer.android.com/build/kotlin-support" ).readText()
371+ val doc = Jsoup .parse(html)
372+ val table =
373+ doc.select(" table" ).find { t ->
374+ val headers = t.select(" th" ).map { it.text() }
375+ headers.any { it.contains(" Kotlin version" , ignoreCase = true ) } &&
376+ headers.any { it.contains(" Required AGP" , ignoreCase = true ) }
377+ } ? : error(" Could not find AGP/Kotlin compatibility table" )
378+
379+ // Cells may carry footnote markers like "8.13.19[1]"; extract the leading x.y[.z] token.
380+ val versionRegex = Regex (""" \d+\.\d+(\.\d+)?""" )
381+ val result = mutableListOf<Pair <Version , Version >>()
382+ for (row in table.select(" tr" ).drop(1 )) {
383+ val cells = row.select(" td" ).map { it.text() }
384+ if (cells.size < 2 ) continue
385+ val kotlinStr = versionRegex.find(cells[0 ])?.value ? : continue
386+ val agpStr = versionRegex.find(cells[1 ])?.value ? : continue
387+ val kotlin =
388+ try {
389+ Version .parse(kotlinStr, strict = false )
390+ } catch (e: Throwable ) {
391+ continue
392+ }
393+ val agp =
394+ try {
395+ Version .parse(agpStr, strict = false )
396+ } catch (e: Throwable ) {
397+ continue
398+ }
399+ result + = agp to kotlin
400+ }
401+ if (result.isEmpty()) error(" No rows parsed from AGP/Kotlin compatibility table" )
402+ return result
403+ }
404+
359405 /* *
360406 * Fetches the value of `SdkConstants.GRADLE_LATEST_VERSION` from Android Studio's source, used to
361407 * resolve `CompatibleGradleVersion.VERSION_FOR_DEV` for bleeding-edge AGP versions.
0 commit comments