Skip to content

Commit

Permalink
Support AGP's built-in Kotlin compilation
Browse files Browse the repository at this point in the history
Previously, the sourceSet-specific KSP configurations (e.g., kspTest)
weren't being created when AGP's built-in Kotlin compilation was
enabled.

This change also increases the AGP version to 8.7.0 in order to test
against a version of AGP with the built-in Kotlin support.

Bug: b/362279380
Test: GradleCompilationTest
  • Loading branch information
scott-pollom committed Oct 4, 2024
1 parent 10657b6 commit 0336500
Show file tree
Hide file tree
Showing 9 changed files with 75 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,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
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,13 @@ class KspConfigurations(private val project: Project) {
}.replaceFirstChar { it.lowercase() }
}

/**
* Returns a new or existing [Configuration] with the given [name], with applied properties.
*/
private fun createConfiguration(
name: String,
readableSetName: String,
): Configuration {
// maybeCreate to be future-proof, but we should never have a duplicate with current logic
return project.configurations.maybeCreate(name).apply {
description = "KSP dependencies for the '$readableSetName' source set."
isCanBeResolved = false // we'll resolve the processor classpath config
Expand All @@ -48,14 +50,19 @@ class KspConfigurations(private val project: Project) {
}
}

private fun getAndroidConfigurationName(target: KotlinTarget, sourceSet: String): String {
/**
* Returns the Android sourceSet-specific KSP configuration name given a [kotlinTarget] and [sourceSet].
*
* For single-platform, [kotlinTarget] can be null.
*/
private fun getAndroidConfigurationName(kotlinTarget: KotlinTarget?, sourceSet: String): String {
val isMain = sourceSet.endsWith("main", ignoreCase = true)
val nameWithoutMain = when {
isMain -> sourceSet.substring(0, sourceSet.length - 4)
else -> sourceSet
}
// Note: on single-platform, target name is conveniently set to "".
return configurationNameOf(PREFIX, target.name, nameWithoutMain)
return configurationNameOf(PREFIX, kotlinTarget?.name ?: "", nameWithoutMain)
}

private fun getKotlinConfigurationName(compilation: KotlinCompilation<*>, sourceSet: KotlinSourceSet): String {
Expand Down Expand Up @@ -86,6 +93,13 @@ class KspConfigurations(private val project: Project) {
// 1.6.0: decorateKotlinProject(project.kotlinExtension)?
decorateKotlinProject(project.extensions.getByName("kotlin") as KotlinProjectExtension, project)
}
// Create sourceSet-specific KSP configurations for the case when the KotlinBaseApiPlugin is applied instead
// of the KotlinBasePluginWrapper (e.g., when AGP's built-in Kotlin support is enabled).
project.plugins.withType(KotlinBaseApiPlugin::class.java) {
// FIXME: After KT-70897 is fixed and AGP's built-in Kotlin support adds a `kotlin` extension, call
// decorateKotlinProject here instead.
createAndroidSourceSetConfigurations(project, kotlinTarget = null)
}
}

private fun decorateKotlinProject(kotlin: KotlinProjectExtension, project: Project) {
Expand Down Expand Up @@ -125,12 +139,7 @@ class KspConfigurations(private val project: Project) {
*/
private fun decorateKotlinTarget(target: KotlinTarget) {
if (target.platformType == KotlinPlatformType.androidJvm) {
AndroidPluginIntegration.forEachAndroidSourceSet(target.project) { sourceSet ->
createConfiguration(
name = getAndroidConfigurationName(target, sourceSet),
readableSetName = "$sourceSet (Android)"
)
}
createAndroidSourceSetConfigurations(target.project, target)
} else {
target.compilations.configureEach { compilation ->
compilation.kotlinSourceSetsObservable.forAll { sourceSet ->
Expand Down Expand Up @@ -177,4 +186,18 @@ class KspConfigurations(private val project: Project) {
compilation.target.project.configurations.findByName(it)
}.toSet()
}

/**
* Creates the Android sourceSet-specific KSP configurations for the given [project] and [kotlinTarget]
*
* For single-platform, [kotlinTarget] can be null.
*/
private fun createAndroidSourceSetConfigurations(project: Project, kotlinTarget: KotlinTarget?) {
AndroidPluginIntegration.forEachAndroidSourceSet(project) { sourceSet ->
createConfiguration(
name = getAndroidConfigurationName(kotlinTarget, sourceSet),
readableSetName = "$sourceSet (Android)"
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -399,4 +399,19 @@ class GradleCompilationTest {

testRule.runner().withArguments().build()
}

/**
* Regression test for b/362279380
*/
@Test
fun androidGradlePluginBuiltInKotlin() {
testRule.setupAppAsAndroidApp(enableAgpBuiltInKotlinSupport = true)
testRule.appModule.dependencies.addAll(
listOf(
artifact(configuration = "ksp", "androidx.room:room-compiler:2.4.2"),
artifact(configuration = "kspTest", "androidx.room:room-compiler:2.4.2")
)
)
testRule.runner().withArguments(":app:assembleDebug").build()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,16 +92,23 @@ class KspIntegrationTestRule(

/**
* Sets up the app module as an android app, adding necessary plugin dependencies, a manifest
* file and necessary gradle configuration.
* file and necessary gradle configuration. If [enableAgpBuiltInKotlinSupport] is true, enable AGP's built-in Kotlin
* support instead of applying the Kotlin Android Gradle plugin.
*/
fun setupAppAsAndroidApp() {
fun setupAppAsAndroidApp(enableAgpBuiltInKotlinSupport: Boolean = false) {
testProject.appModule.plugins.addAll(
listOf(
PluginDeclaration.id("com.android.application", testConfig.androidBaseVersion),
PluginDeclaration.kotlin("android", testConfig.kotlinBaseVersion),
PluginDeclaration.id("com.google.devtools.ksp", testConfig.kspVersion)
)
)
if (enableAgpBuiltInKotlinSupport) {
testProject.appModule
.plugins
.add(PluginDeclaration.id("com.android.experimental.built-in-kotlin", testConfig.androidBaseVersion))
} else {
testProject.appModule.plugins.add(PluginDeclaration.kotlin("android", testConfig.kotlinBaseVersion))
}
addAndroidBoilerplate()
}

Expand All @@ -126,11 +133,15 @@ class KspIntegrationTestRule(
"""
android {
namespace = "com.example.kspandroidtestapp"
compileSdkVersion(31)
compileSdk = 31
defaultConfig {
minSdkVersion(24)
minSdk = 24
}
}
dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib:${testConfig.kotlinBaseVersion}")
}
""".trimIndent()
)
testProject.appModule.moduleRoot.resolve("src/main/AndroidManifest.xml")
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
org.gradle.jvmargs=-Duser.country=US -Dkotlin.daemon.jvm.options=-Xmx4096m -Dfile.encoding=UTF-8

kotlinBaseVersion=2.1.0-dev-5441
agpBaseVersion=8.0.2
agpBaseVersion=8.7.0
intellijVersion=233.13135.103
junitVersion=4.13.1
junit5Version=5.8.2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ dependencies {
android {
namespace = "com.example.kspandroidtestapp"
defaultConfig {
minSdkVersion(24)
minSdk = 24
}
compileSdk = 34
buildFeatures {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ dependencies {

android {
namespace = "com.example.myapplication"
compileSdkVersion(34)
compileSdk = 34
defaultConfig {
applicationId = "org.gradle.kotlin.dsl.samples.androidstudio"
minSdkVersion(34)
targetSdkVersion(34)
minSdk = 34
targetSdk = 34
versionCode = 1
versionName = "1.0"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ dependencies {

android {
namespace = "com.example.mylibrary"
compileSdkVersion(34)
compileSdk = 34
defaultConfig {
minSdkVersion(34)
targetSdkVersion(34)
minSdk = 34
targetSdk = 34
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ dependencies {

android {
namespace = "com.example.myapplication"
compileSdkVersion(34)
compileSdk = 34
defaultConfig {
applicationId = "org.gradle.kotlin.dsl.samples.androidstudio"
minSdkVersion(34)
targetSdkVersion(34)
minSdk = 34
targetSdk = 34
versionCode = 1
versionName = "1.0"
}
Expand Down

0 comments on commit 0336500

Please sign in to comment.