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 2, 2024
1 parent 117f0b4 commit c5a041d
Show file tree
Hide file tree
Showing 10 changed files with 75 additions and 26 deletions.
Empty file.
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,19 @@ 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,9 @@ dependencies {

android {
namespace = "com.example.mylibrary"
compileSdkVersion(34)
compileSdk = 34
defaultConfig {
minSdkVersion(34)
targetSdkVersion(34)
minSdk = 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 c5a041d

Please sign in to comment.