Skip to content

Commit

Permalink
Partially migrate KSP off AGP's legacy Variant API
Browse files Browse the repository at this point in the history
Use AGP's new addKspConfigurations() API when possible.

Bug: #2250
Test: AGP741IT and existing ProcessorClasspathConfigurationsTest
  • Loading branch information
scott-pollom committed Jan 31, 2025
1 parent 553adb6 commit 425037d
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 19 deletions.
6 changes: 6 additions & 0 deletions gradle-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

description = "Kotlin Symbol Processor"
Expand Down Expand Up @@ -126,6 +127,8 @@ java {
resources.srcDir(testPropsOutDir)
}
}
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}

tasks.named("compileTestKotlin").configure {
Expand Down Expand Up @@ -181,4 +184,7 @@ kotlin {
kotlin.srcDir(writeVersionSrcTask)
}
}
compilerOptions {
jvmTarget.set(JvmTarget.JVM_11)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ object AndroidPluginIntegration {
private fun decorateAndroidExtension(project: Project, onSourceSet: (String) -> Unit) {
val sourceSets = when (val androidExt = project.extensions.getByName("android")) {
is BaseExtension -> androidExt.sourceSets
is CommonExtension<*, *, *, *> -> androidExt.sourceSets
is CommonExtension<*, *, *, *, *, *> -> androidExt.sourceSets
else -> throw RuntimeException("Unsupported Android Gradle plugin version.")
}
sourceSets.all {
Expand Down Expand Up @@ -155,4 +155,28 @@ object AndroidPluginIntegration {
resourcesOutputDir
)
}

/**
* Returns false for AGP versions 8.10.0-alpha01 or higher.
*
* Returns true for older AGP versions or when AGP version cannot be determined.
*/
val useLegacyVariantApi: Boolean by lazy {
val agpVersion = try {
val versionClass = Class.forName("com.android.Version")
val versionField = versionClass.getField("ANDROID_GRADLE_PLUGIN_VERSION")
versionField.get(null) as String
} catch (e: Exception) {
// AGP not applied or version field not found
null
}

// Fall back to using the legacy Variant API if can't determine AGP version for now
if (agpVersion == null) {
return@lazy true
}
val agpMajorVersion = agpVersion.split(".").getOrNull(0)?.toIntOrNull() ?: 0
val agpMinorVersion = agpVersion.split(".").getOrNull(1)?.toIntOrNull() ?: 0
return@lazy agpMajorVersion < 8 || agpMajorVersion == 8 && agpMinorVersion < 10
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.google.devtools.ksp.gradle

import com.android.build.gradle.api.AndroidBasePlugin
import com.google.devtools.ksp.gradle.AndroidPluginIntegration.useLegacyVariantApi
import org.gradle.api.InvalidUserCodeException
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
Expand Down Expand Up @@ -100,6 +102,13 @@ class KspConfigurations(private val project: Project) {
// decorateKotlinProject here instead.
createAndroidSourceSetConfigurations(project, kotlinTarget = null)
}
if (!useLegacyVariantApi) {
project.plugins.withType(AndroidBasePlugin::class.java) {
val androidComponents =
project.extensions.findByType(com.android.build.api.variant.AndroidComponentsExtension::class.java)
androidComponents?.addKspConfigurations(useGlobalConfiguration = allowAllTargetConfiguration)
}
}
}

private fun decorateKotlinProject(kotlin: KotlinProjectExtension, project: Project) {
Expand Down Expand Up @@ -139,7 +148,9 @@ class KspConfigurations(private val project: Project) {
*/
private fun decorateKotlinTarget(target: KotlinTarget) {
if (target.platformType == KotlinPlatformType.androidJvm) {
createAndroidSourceSetConfigurations(target.project, target)
if (useLegacyVariantApi) {
createAndroidSourceSetConfigurations(target.project, target)
}
} else {
target.compilations.configureEach { compilation ->
compilation.kotlinSourceSetsObservable.forAll { sourceSet ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package com.google.devtools.ksp.gradle

import com.google.devtools.ksp.KspExperimental
import com.google.devtools.ksp.gradle.model.builder.KspModelBuilder
import com.google.devtools.ksp.gradle.AndroidPluginIntegration.useLegacyVariantApi
import org.gradle.api.Action
import org.gradle.api.Project
import org.gradle.api.Task
Expand Down Expand Up @@ -253,17 +254,25 @@ class KspGradleSubplugin @Inject internal constructor(private val registry: Tool
val kotlinCompileProvider: TaskProvider<AbstractKotlinCompileTool<*>> =
project.locateTask(kotlinCompilation.compileKotlinTaskName) ?: return project.provider { emptyList() }
val kspExtension = project.extensions.getByType(KspExtension::class.java)
val kspConfigurations = kspConfigurations.find(kotlinCompilation)
val nonEmptyKspConfigurations = kspConfigurations.filter { it.allDependencies.isNotEmpty() }
if (nonEmptyKspConfigurations.isEmpty()) {
return project.provider { emptyList() }
}
if (kotlinCompileProvider.name == "compileKotlinMetadata") {
return project.provider { emptyList() }
}
if ((kotlinCompilation as? KotlinSharedNativeCompilation)?.platformType == KotlinPlatformType.common) {
return project.provider { emptyList() }
}
assert(kotlinCompileProvider.name.startsWith("compile"))
val kspTaskName = kotlinCompileProvider.name.replaceFirst("compile", "ksp")
val processorClasspath =
project.configurations.maybeCreate("${kspTaskName}ProcessorClasspath").markResolvable()
if (kotlinCompilation.platformType != KotlinPlatformType.androidJvm || useLegacyVariantApi) {
val nonEmptyKspConfigurations =
kspConfigurations.find(kotlinCompilation)
.filter { it.allDependencies.isNotEmpty() }
if (nonEmptyKspConfigurations.isEmpty()) {
return project.provider { emptyList() }
}
processorClasspath.extendsFrom(*nonEmptyKspConfigurations.toTypedArray())
}

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

assert(kotlinCompileProvider.name.startsWith("compile"))
val kspTaskName = kotlinCompileProvider.name.replaceFirst("compile", "ksp")

val processorClasspath = project.configurations.maybeCreate("${kspTaskName}ProcessorClasspath")
.extendsFrom(*nonEmptyKspConfigurations.toTypedArray()).markResolvable()

val kspCachesDir = getKspCachesDir(project, sourceSetName, target)
fun configureAsKspTask(kspTask: KspTask, isIncremental: Boolean) {
// depends on the processor; if the processor changes, it needs to be reprocessed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ data class TestConfig(
}

val androidBaseVersion by lazy {
kspProjectProperties["agpTestVersion"] as String
kspProjectProperties["agpBaseVersion"] as String
}

val mavenRepoPath = mavenRepoDir.path.replace(File.separatorChar, '/')
Expand Down
3 changes: 1 addition & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
org.gradle.jvmargs=-Duser.country=US -Dkotlin.daemon.jvm.options=-Xmx4096m -Dfile.encoding=UTF-8

kotlinBaseVersion=2.1.20-dev-8066
agpBaseVersion=7.3.1
agpTestVersion=8.7.1
agpBaseVersion=8.10.0-alpha03
intellijVersion=233.13135.128
junitVersion=4.13.1
junit5Version=5.8.2
Expand Down
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
16 changes: 14 additions & 2 deletions integration-tests/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import com.google.devtools.ksp.RelativizingInternalPathProvider
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import kotlin.math.max

val junitVersion: String by project
val kotlinBaseVersion: String by project
val agpTestVersion: String by project
val agpBaseVersion: String by project
val aaCoroutinesVersion: String by project

plugins {
Expand All @@ -25,7 +26,7 @@ dependencies {
fun Test.configureCommonSettings() {
systemProperty("kotlinVersion", kotlinBaseVersion)
systemProperty("kspVersion", version)
systemProperty("agpVersion", agpTestVersion)
systemProperty("agpVersion", agpBaseVersion)
jvmArgumentProviders.add(
RelativizingInternalPathProvider(
"testRepo",
Expand Down Expand Up @@ -69,3 +70,14 @@ tasks.named<Test>("test") {
// Ensure that 'test' depends on 'compatibilityTest'
dependsOn(agpCompatibilityTest)
}

java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}

kotlin {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_11)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,90 @@ class AGP741IT(useKSP2: Boolean) {
}
}

/**
* Similar to ProcessorClasspathConfigurationsTest.testConfigurationsForAndroidApp(), but we want to test with AGP
* version < 8.10.0 too because we use AGP's legacy Variant API in that case.
*/
@Test
fun testConfigurationsForAndroidApp() {
val gradleRunner = GradleRunner.create().withProjectDir(project.root).withGradleVersion("7.6.3")

File(project.root, "gradle.properties").appendText("\nagpVersion=7.4.1")
File(project.root, "workload/build.gradle.kts").appendText(
"""
android {
flavorDimensions += listOf("tier", "region")
productFlavors {
create("free") {
dimension = "tier"
}
create("premium") {
dimension = "tier"
}
create("us") {
dimension = "region"
}
create("eu") {
dimension = "region"
}
}
}
configurations.matching { it.name.startsWith("ksp") && !it.name.endsWith("ProcessorClasspath") }.all {
// Make sure ksp configs are not empty.
project.dependencies.add(name, "androidx.room:room-compiler:2.4.2")
}
tasks.register("testConfigurations") {
// Resolve all tasks to trigger classpath config creation
dependsOn(tasks["tasks"])
doLast {
val freeUsDebugConfig = configurations["kspFreeUsDebugKotlinProcessorClasspath"]
val testFreeUsDebugConfig = configurations["kspFreeUsDebugUnitTestKotlinProcessorClasspath"]
val androidTestFreeUsDebugConfig =
configurations["kspFreeUsDebugAndroidTestKotlinProcessorClasspath"]
val freeUsDebugParentConfigs =
setOf(
"ksp",
"kspDebug",
"kspFree",
"kspUs",
"kspFreeUs",
"kspFreeUsDebug"
)
val testFreeUsDebugParentConfigs =
setOf(
"ksp",
"kspTest",
"kspTestDebug",
"kspTestFree",
"kspTestUs",
"kspTestFreeUs",
"kspTestFreeUsDebug"
)
val androidTestFreeUsDebugParentConfigs =
setOf(
"ksp",
"kspAndroidTest",
"kspAndroidTestDebug",
"kspAndroidTestFree",
"kspAndroidTestUs",
"kspAndroidTestFreeUs",
"kspAndroidTestFreeUsDebug"
)
require(freeUsDebugConfig.extendsFrom.map { it.name }.toSet() == freeUsDebugParentConfigs)
require(
testFreeUsDebugConfig.extendsFrom.map { it.name }.toSet() == testFreeUsDebugParentConfigs
)
require(
androidTestFreeUsDebugConfig.extendsFrom.map { it.name }.toSet() == androidTestFreeUsDebugParentConfigs
)
}
}
""".trimIndent()
)

gradleRunner.withArguments(":workload:testConfigurations").build()
}

companion object {
@JvmStatic
@Parameterized.Parameters(name = "KSP2={0}")
Expand Down

0 comments on commit 425037d

Please sign in to comment.