Skip to content

Commit 6f1cd08

Browse files
scott-pollomting-yuan
authored andcommitted
Partially migrate KSP off AGP's legacy Variant API
Use AGP's new addKspConfigurations() API when possible. Bug: #2250 Test: AGP741IT and existing ProcessorClasspathConfigurationsTest (cherry picked from commit b6b8461)
1 parent d27912e commit 6f1cd08

File tree

10 files changed

+165
-23
lines changed

10 files changed

+165
-23
lines changed

gradle-plugin/build.gradle.kts

Lines changed: 6 additions & 0 deletions
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
}

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

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*/
1717
package com.google.devtools.ksp.gradle
1818

19+
import com.android.build.api.AndroidPluginVersion
1920
import com.android.build.api.dsl.CommonExtension
2021
import com.android.build.gradle.BaseExtension
2122
import com.android.build.gradle.api.SourceKind
@@ -49,7 +50,7 @@ object AndroidPluginIntegration {
4950
private fun decorateAndroidExtension(project: Project, onSourceSet: (String) -> Unit) {
5051
val sourceSets = when (val androidExt = project.extensions.getByName("android")) {
5152
is BaseExtension -> androidExt.sourceSets
52-
is CommonExtension<*, *, *, *> -> androidExt.sourceSets
53+
is CommonExtension<*, *, *, *, *, *> -> androidExt.sourceSets
5354
else -> throw RuntimeException("Unsupported Android Gradle plugin version.")
5455
}
5556
sourceSets.all {
@@ -155,4 +156,26 @@ object AndroidPluginIntegration {
155156
resourcesOutputDir
156157
)
157158
}
159+
160+
/**
161+
* Returns false for AGP versions 8.10.0-alpha03 or higher.
162+
*
163+
* Returns true for older AGP versions or when AGP version cannot be determined.
164+
*/
165+
fun Project.useLegacyVariantApi(): Boolean {
166+
val agpVersion = try {
167+
this.extensions
168+
.findByType(com.android.build.api.variant.AndroidComponentsExtension::class.java)
169+
?.pluginVersion
170+
} catch (e: Exception) {
171+
// Perhaps a version of AGP before pluginVersion API was added.
172+
null
173+
}
174+
175+
// Fall back to using the legacy Variant API if the AGP version can't be determined for now.
176+
if (agpVersion == null) {
177+
return true
178+
}
179+
return agpVersion < AndroidPluginVersion(8, 10, 0).alpha(3)
180+
}
158181
}

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

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.google.devtools.ksp.gradle
22

3+
import com.google.devtools.ksp.gradle.AndroidPluginIntegration.useLegacyVariantApi
34
import org.gradle.api.InvalidUserCodeException
45
import org.gradle.api.Project
56
import org.gradle.api.artifacts.Configuration
@@ -100,13 +101,20 @@ class KspConfigurations(private val project: Project) {
100101
// decorateKotlinProject here instead.
101102
createAndroidSourceSetConfigurations(project, kotlinTarget = null)
102103
}
104+
project.pluginManager.withPlugin("com.android.base") {
105+
if (!project.useLegacyVariantApi()) {
106+
val androidComponents =
107+
project.extensions.findByType(com.android.build.api.variant.AndroidComponentsExtension::class.java)
108+
androidComponents?.addKspConfigurations(useGlobalConfiguration = allowAllTargetConfiguration)
109+
}
110+
}
103111
}
104112

105113
private fun decorateKotlinProject(kotlin: KotlinProjectExtension, project: Project) {
106114
when (kotlin) {
107-
is KotlinSingleTargetExtension<*> -> decorateKotlinTarget(kotlin.target)
115+
is KotlinSingleTargetExtension<*> -> decorateKotlinTarget(kotlin.target, isKotlinMultiplatform = false)
108116
is KotlinMultiplatformExtension -> {
109-
kotlin.targets.configureEach(::decorateKotlinTarget)
117+
kotlin.targets.configureEach { decorateKotlinTarget(it, isKotlinMultiplatform = true) }
110118

111119
var reported = false
112120
configurationForAll.dependencies.whenObjectAdded {
@@ -137,9 +145,11 @@ class KspConfigurations(private val project: Project) {
137145
* there are slight differences between the two - Kotlin creates some extra sets with unexpected word ordering,
138146
* and things get worse when you add product flavors. So, we use AGP sets as the source of truth.
139147
*/
140-
private fun decorateKotlinTarget(target: KotlinTarget) {
148+
private fun decorateKotlinTarget(target: KotlinTarget, isKotlinMultiplatform: Boolean) {
141149
if (target.platformType == KotlinPlatformType.androidJvm) {
142-
createAndroidSourceSetConfigurations(target.project, target)
150+
if (project.useLegacyVariantApi() || isKotlinMultiplatform) {
151+
createAndroidSourceSetConfigurations(target.project, target)
152+
}
143153
} else {
144154
target.compilations.configureEach { compilation ->
145155
compilation.kotlinSourceSetsObservable.forAll { sourceSet ->

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

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.google.devtools.ksp.gradle
1818

1919
import com.google.devtools.ksp.KspExperimental
20+
import com.google.devtools.ksp.gradle.AndroidPluginIntegration.useLegacyVariantApi
2021
import com.google.devtools.ksp.gradle.model.builder.KspModelBuilder
2122
import org.gradle.api.Action
2223
import org.gradle.api.Project
@@ -253,17 +254,30 @@ 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 ||
268+
project.useLegacyVariantApi() ||
269+
project.pluginManager.hasPlugin("kotlin-multiplatform")
270+
) {
271+
val nonEmptyKspConfigurations =
272+
kspConfigurations.find(kotlinCompilation)
273+
.filter { it.allDependencies.isNotEmpty() }
274+
if (nonEmptyKspConfigurations.isEmpty()) {
275+
return project.provider { emptyList() }
276+
}
277+
processorClasspath.extendsFrom(*nonEmptyKspConfigurations.toTypedArray())
278+
} else if (processorClasspath.allDependencies.isEmpty()) {
279+
return project.provider { emptyList() }
280+
}
267281

268282
val target = kotlinCompilation.target.name
269283
val sourceSetName = kotlinCompilation.defaultSourceSet.name
@@ -297,12 +311,6 @@ class KspGradleSubplugin @Inject internal constructor(private val registry: Tool
297311
"$KSP_GROUP_ID:$KSP_COMPILER_PLUGIN_ID_NON_EMBEDDABLE:$KSP_VERSION"
298312
)
299313

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-
306314
val kspCachesDir = getKspCachesDir(project, sourceSetName, target)
307315
fun configureAsKspTask(kspTask: KspTask, isIncremental: Boolean) {
308316
// depends on the processor; if the processor changes, it needs to be reprocessed.

gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/SourceSetConfigurationsTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ class SourceSetConfigurationsTest(val useKSP2: Boolean) {
300300
it.startsWith("kapt") && !it.startsWith("kaptClasspath_")
301301
}
302302
val kspConfigurations = configurations.filter {
303-
it.startsWith("ksp")
303+
it.startsWith("ksp") && !it.endsWith("KotlinProcessorClasspath")
304304
}
305305
assertThat(kspConfigurations).containsExactlyElementsIn(
306306
kaptConfigurations.map {

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

Lines changed: 1 addition & 1 deletion
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, '/')

gradle.properties

Lines changed: 1 addition & 2 deletions
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.0
5-
agpBaseVersion=7.3.1
6-
agpTestVersion=8.7.1
5+
agpBaseVersion=8.10.0-alpha03
76
intellijVersion=233.13135.128
87
junitVersion=4.13.1
98
junit5Version=5.8.2

gradle/wrapper/gradle-wrapper.properties

Lines changed: 1 addition & 1 deletion
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

integration-tests/build.gradle.kts

Lines changed: 14 additions & 2 deletions
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+
}

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

Lines changed: 84 additions & 0 deletions
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)