Skip to content

Commit f95cf25

Browse files
committed
Partially migrate KSP off AGP's legacy Variant API
Use AGP's new addKspConfigurations() API when possible. Bug: #2250 Test: AGP741IT and existing ProcessorClasspathConfigurationsTest
1 parent 0dfb6de commit f95cf25

File tree

9 files changed

+158
-19
lines changed

9 files changed

+158
-19
lines changed

Diff for: gradle-plugin/build.gradle.kts

+6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
12
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
23

34
description = "Kotlin Symbol Processor"
@@ -126,6 +127,8 @@ java {
126127
resources.srcDir(testPropsOutDir)
127128
}
128129
}
130+
sourceCompatibility = JavaVersion.VERSION_11
131+
targetCompatibility = JavaVersion.VERSION_11
129132
}
130133

131134
tasks.named("compileTestKotlin").configure {
@@ -181,4 +184,7 @@ kotlin {
181184
kotlin.srcDir(writeVersionSrcTask)
182185
}
183186
}
187+
compilerOptions {
188+
jvmTarget.set(JvmTarget.JVM_11)
189+
}
184190
}

Diff for: gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/AndroidPluginIntegration.kt

+25-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ object AndroidPluginIntegration {
4949
private fun decorateAndroidExtension(project: Project, onSourceSet: (String) -> Unit) {
5050
val sourceSets = when (val androidExt = project.extensions.getByName("android")) {
5151
is BaseExtension -> androidExt.sourceSets
52-
is CommonExtension<*, *, *, *> -> androidExt.sourceSets
52+
is CommonExtension<*, *, *, *, *, *> -> androidExt.sourceSets
5353
else -> throw RuntimeException("Unsupported Android Gradle plugin version.")
5454
}
5555
sourceSets.all {
@@ -155,4 +155,28 @@ object AndroidPluginIntegration {
155155
resourcesOutputDir
156156
)
157157
}
158+
159+
/**
160+
* Returns false for AGP versions 8.10.0-alpha01 or higher.
161+
*
162+
* Returns true for older AGP versions or when AGP version cannot be determined.
163+
*/
164+
val useLegacyVariantApi: Boolean by lazy {
165+
val agpVersion = try {
166+
val versionClass = Class.forName("com.android.Version")
167+
val versionField = versionClass.getField("ANDROID_GRADLE_PLUGIN_VERSION")
168+
versionField.get(null) as String
169+
} catch (e: Exception) {
170+
// AGP not applied or version field not found
171+
null
172+
}
173+
174+
// Fall back to using the legacy Variant API if can't determine AGP version for now
175+
if (agpVersion == null) {
176+
return@lazy true
177+
}
178+
val agpMajorVersion = agpVersion.split(".").getOrNull(0)?.toIntOrNull() ?: 0
179+
val agpMinorVersion = agpVersion.split(".").getOrNull(1)?.toIntOrNull() ?: 0
180+
return@lazy agpMajorVersion < 8 || agpMajorVersion == 8 && agpMinorVersion < 10
181+
}
158182
}

Diff for: gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspConfigurations.kt

+12-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.google.devtools.ksp.gradle
22

3+
import com.android.build.gradle.api.AndroidBasePlugin
4+
import com.google.devtools.ksp.gradle.AndroidPluginIntegration.useLegacyVariantApi
35
import org.gradle.api.InvalidUserCodeException
46
import org.gradle.api.Project
57
import org.gradle.api.artifacts.Configuration
@@ -100,6 +102,13 @@ class KspConfigurations(private val project: Project) {
100102
// decorateKotlinProject here instead.
101103
createAndroidSourceSetConfigurations(project, kotlinTarget = null)
102104
}
105+
if (!useLegacyVariantApi) {
106+
project.plugins.withType(AndroidBasePlugin::class.java) {
107+
val androidComponents =
108+
project.extensions.findByType(com.android.build.api.variant.AndroidComponentsExtension::class.java)
109+
androidComponents?.addKspConfigurations(useGlobalConfiguration = allowAllTargetConfiguration)
110+
}
111+
}
103112
}
104113

105114
private fun decorateKotlinProject(kotlin: KotlinProjectExtension, project: Project) {
@@ -139,7 +148,9 @@ class KspConfigurations(private val project: Project) {
139148
*/
140149
private fun decorateKotlinTarget(target: KotlinTarget) {
141150
if (target.platformType == KotlinPlatformType.androidJvm) {
142-
createAndroidSourceSetConfigurations(target.project, target)
151+
if (useLegacyVariantApi) {
152+
createAndroidSourceSetConfigurations(target.project, target)
153+
}
143154
} else {
144155
target.compilations.configureEach { compilation ->
145156
compilation.kotlinSourceSetsObservable.forAll { sourceSet ->

Diff for: gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspSubplugin.kt

+14-11
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package com.google.devtools.ksp.gradle
1818

1919
import com.google.devtools.ksp.KspExperimental
2020
import com.google.devtools.ksp.gradle.model.builder.KspModelBuilder
21+
import com.google.devtools.ksp.gradle.AndroidPluginIntegration.useLegacyVariantApi
2122
import org.gradle.api.Action
2223
import org.gradle.api.Project
2324
import org.gradle.api.Task
@@ -253,17 +254,25 @@ class KspGradleSubplugin @Inject internal constructor(private val registry: Tool
253254
val kotlinCompileProvider: TaskProvider<AbstractKotlinCompileTool<*>> =
254255
project.locateTask(kotlinCompilation.compileKotlinTaskName) ?: return project.provider { emptyList() }
255256
val kspExtension = project.extensions.getByType(KspExtension::class.java)
256-
val kspConfigurations = kspConfigurations.find(kotlinCompilation)
257-
val nonEmptyKspConfigurations = kspConfigurations.filter { it.allDependencies.isNotEmpty() }
258-
if (nonEmptyKspConfigurations.isEmpty()) {
259-
return project.provider { emptyList() }
260-
}
261257
if (kotlinCompileProvider.name == "compileKotlinMetadata") {
262258
return project.provider { emptyList() }
263259
}
264260
if ((kotlinCompilation as? KotlinSharedNativeCompilation)?.platformType == KotlinPlatformType.common) {
265261
return project.provider { emptyList() }
266262
}
263+
assert(kotlinCompileProvider.name.startsWith("compile"))
264+
val kspTaskName = kotlinCompileProvider.name.replaceFirst("compile", "ksp")
265+
val processorClasspath =
266+
project.configurations.maybeCreate("${kspTaskName}ProcessorClasspath").markResolvable()
267+
if (kotlinCompilation.platformType != KotlinPlatformType.androidJvm || useLegacyVariantApi) {
268+
val nonEmptyKspConfigurations =
269+
kspConfigurations.find(kotlinCompilation)
270+
.filter { it.allDependencies.isNotEmpty() }
271+
if (nonEmptyKspConfigurations.isEmpty()) {
272+
return project.provider { emptyList() }
273+
}
274+
processorClasspath.extendsFrom(*nonEmptyKspConfigurations.toTypedArray())
275+
}
267276

268277
val target = kotlinCompilation.target.name
269278
val sourceSetName = kotlinCompilation.defaultSourceSet.name
@@ -297,12 +306,6 @@ class KspGradleSubplugin @Inject internal constructor(private val registry: Tool
297306
"$KSP_GROUP_ID:$KSP_COMPILER_PLUGIN_ID_NON_EMBEDDABLE:$KSP_VERSION"
298307
)
299308

300-
assert(kotlinCompileProvider.name.startsWith("compile"))
301-
val kspTaskName = kotlinCompileProvider.name.replaceFirst("compile", "ksp")
302-
303-
val processorClasspath = project.configurations.maybeCreate("${kspTaskName}ProcessorClasspath")
304-
.extendsFrom(*nonEmptyKspConfigurations.toTypedArray()).markResolvable()
305-
306309
val kspCachesDir = getKspCachesDir(project, sourceSetName, target)
307310
fun configureAsKspTask(kspTask: KspTask, isIncremental: Boolean) {
308311
// depends on the processor; if the processor changes, it needs to be reprocessed.

Diff for: gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/testing/TestConfig.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ data class TestConfig(
5555
}
5656

5757
val androidBaseVersion by lazy {
58-
kspProjectProperties["agpTestVersion"] as String
58+
kspProjectProperties["agpBaseVersion"] as String
5959
}
6060

6161
val mavenRepoPath = mavenRepoDir.path.replace(File.separatorChar, '/')

Diff for: gradle.properties

+1-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22
org.gradle.jvmargs=-Duser.country=US -Dkotlin.daemon.jvm.options=-Xmx4096m -Dfile.encoding=UTF-8
33

44
kotlinBaseVersion=2.1.20-dev-8066
5-
agpBaseVersion=7.3.1
6-
agpTestVersion=8.7.1
5+
agpBaseVersion=8.10.0-alpha02
76
intellijVersion=233.13135.128
87
junitVersion=4.13.1
98
junit5Version=5.8.2

Diff for: gradle/wrapper/gradle-wrapper.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip
44
networkTimeout=10000
55
validateDistributionUrl=true
66
zipStoreBase=GRADLE_USER_HOME

Diff for: integration-tests/build.gradle.kts

+14-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import com.google.devtools.ksp.RelativizingInternalPathProvider
2+
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
23
import kotlin.math.max
34

45
val junitVersion: String by project
56
val kotlinBaseVersion: String by project
6-
val agpTestVersion: String by project
7+
val agpBaseVersion: String by project
78
val aaCoroutinesVersion: String by project
89

910
plugins {
@@ -25,7 +26,7 @@ dependencies {
2526
fun Test.configureCommonSettings() {
2627
systemProperty("kotlinVersion", kotlinBaseVersion)
2728
systemProperty("kspVersion", version)
28-
systemProperty("agpVersion", agpTestVersion)
29+
systemProperty("agpVersion", agpBaseVersion)
2930
jvmArgumentProviders.add(
3031
RelativizingInternalPathProvider(
3132
"testRepo",
@@ -69,3 +70,14 @@ tasks.named<Test>("test") {
6970
// Ensure that 'test' depends on 'compatibilityTest'
7071
dependsOn(agpCompatibilityTest)
7172
}
73+
74+
java {
75+
sourceCompatibility = JavaVersion.VERSION_11
76+
targetCompatibility = JavaVersion.VERSION_11
77+
}
78+
79+
kotlin {
80+
compilerOptions {
81+
jvmTarget.set(JvmTarget.JVM_11)
82+
}
83+
}

Diff for: integration-tests/src/test/kotlin/com/google/devtools/ksp/test/AGP741IT.kt

+84
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,90 @@ class AGP741IT(useKSP2: Boolean) {
4141
}
4242
}
4343

44+
/**
45+
* Similar to ProcessorClasspathConfigurationsTest.testConfigurationsForAndroidApp(), but we want to test with AGP
46+
* version < 8.10.0 too because we use AGP's legacy Variant API in that case.
47+
*/
48+
@Test
49+
fun testConfigurationsForAndroidApp() {
50+
val gradleRunner = GradleRunner.create().withProjectDir(project.root).withGradleVersion("7.6.3")
51+
52+
File(project.root, "gradle.properties").appendText("\nagpVersion=7.4.1")
53+
File(project.root, "workload/build.gradle.kts").appendText(
54+
"""
55+
android {
56+
flavorDimensions += listOf("tier", "region")
57+
productFlavors {
58+
create("free") {
59+
dimension = "tier"
60+
}
61+
create("premium") {
62+
dimension = "tier"
63+
}
64+
create("us") {
65+
dimension = "region"
66+
}
67+
create("eu") {
68+
dimension = "region"
69+
}
70+
}
71+
}
72+
configurations.matching { it.name.startsWith("ksp") && !it.name.endsWith("ProcessorClasspath") }.all {
73+
// Make sure ksp configs are not empty.
74+
project.dependencies.add(name, "androidx.room:room-compiler:2.4.2")
75+
}
76+
tasks.register("testConfigurations") {
77+
// Resolve all tasks to trigger classpath config creation
78+
dependsOn(tasks["tasks"])
79+
doLast {
80+
val freeUsDebugConfig = configurations["kspFreeUsDebugKotlinProcessorClasspath"]
81+
val testFreeUsDebugConfig = configurations["kspFreeUsDebugUnitTestKotlinProcessorClasspath"]
82+
val androidTestFreeUsDebugConfig =
83+
configurations["kspFreeUsDebugAndroidTestKotlinProcessorClasspath"]
84+
val freeUsDebugParentConfigs =
85+
setOf(
86+
"ksp",
87+
"kspDebug",
88+
"kspFree",
89+
"kspUs",
90+
"kspFreeUs",
91+
"kspFreeUsDebug"
92+
)
93+
val testFreeUsDebugParentConfigs =
94+
setOf(
95+
"ksp",
96+
"kspTest",
97+
"kspTestDebug",
98+
"kspTestFree",
99+
"kspTestUs",
100+
"kspTestFreeUs",
101+
"kspTestFreeUsDebug"
102+
)
103+
val androidTestFreeUsDebugParentConfigs =
104+
setOf(
105+
"ksp",
106+
"kspAndroidTest",
107+
"kspAndroidTestDebug",
108+
"kspAndroidTestFree",
109+
"kspAndroidTestUs",
110+
"kspAndroidTestFreeUs",
111+
"kspAndroidTestFreeUsDebug"
112+
)
113+
require(freeUsDebugConfig.extendsFrom.map { it.name }.toSet() == freeUsDebugParentConfigs)
114+
require(
115+
testFreeUsDebugConfig.extendsFrom.map { it.name }.toSet() == testFreeUsDebugParentConfigs
116+
)
117+
require(
118+
androidTestFreeUsDebugConfig.extendsFrom.map { it.name }.toSet() == androidTestFreeUsDebugParentConfigs
119+
)
120+
}
121+
}
122+
""".trimIndent()
123+
)
124+
125+
gradleRunner.withArguments(":workload:testConfigurations").build()
126+
}
127+
44128
companion object {
45129
@JvmStatic
46130
@Parameterized.Parameters(name = "KSP2={0}")

0 commit comments

Comments
 (0)