Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[gradle] Support new AGP with androidLibrary target #5157

Merged
merged 1 commit into from
Oct 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/gradle-plugin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-20.04, macos-14, windows-2022]
gradle: [7.4, 8.8]
agp: [8.1.0, 8.5.0]
gradle: [7.4, 8.10.2]
agp: [8.1.0, 8.5.0, 8.8.0-alpha08]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
Expand Down
19 changes: 11 additions & 8 deletions gradle-plugins/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import com.gradle.publish.PluginBundleExtension
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile

plugins {
Expand All @@ -20,8 +21,8 @@ subprojects {

plugins.withId("java") {
configureIfExists<JavaPluginExtension> {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11

withJavadocJar()
withSourcesJar()
Expand All @@ -30,11 +31,13 @@ subprojects {

plugins.withId("org.jetbrains.kotlin.jvm") {
tasks.withType(KotlinJvmCompile::class).configureEach {
// must be set to a language version of the kotlin compiler & runtime,
// which is bundled to the oldest supported Gradle
kotlinOptions.languageVersion = "1.5"
kotlinOptions.apiVersion = "1.5"
kotlinOptions.jvmTarget = "1.8"
compilerOptions {
// must be set to a language version of the kotlin compiler & runtime,
// which is bundled to the oldest supported Gradle
languageVersion.set(KotlinVersion.KOTLIN_1_5)
apiVersion.set(KotlinVersion.KOTLIN_1_5)
jvmTarget.set(JvmTarget.JVM_11)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package org.jetbrains.compose.resources

import com.android.build.api.dsl.KotlinMultiplatformAndroidTarget
import com.android.build.api.variant.AndroidComponentsExtension
import com.android.build.api.variant.Component
import com.android.build.api.variant.HasAndroidTest
import com.android.build.api.variant.KotlinMultiplatformAndroidComponentsExtension
import com.android.build.api.variant.Sources
import com.android.build.gradle.internal.lint.AndroidLintAnalysisTask
import com.android.build.gradle.internal.lint.LintModelWriterTask
import org.gradle.api.DefaultTask
Expand All @@ -25,37 +27,130 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
import java.io.File
import javax.inject.Inject

internal fun Project.configureAndroidComposeResources(moduleResourceDir: Provider<File>? = null) {
//copy all compose resources to android assets
val androidComponents = project.extensions.findByType(AndroidComponentsExtension::class.java) ?: return
//copy all compose resources to android assets
internal fun Project.configureAndroidComposeResources(
agpPluginId: String,
moduleResourceDir: Provider<File>? = null
) {
val kotlinExtension = extensions.findByType(KotlinMultiplatformExtension::class.java) ?: return

if (agpPluginId != AGP_KMP_LIB_ID) {
extensions.findByType(AndroidComponentsExtension::class.java)?.let { androidComponents ->
configureAndroidComposeResources(kotlinExtension, androidComponents, moduleResourceDir)
}
} else {
@Suppress("UnstableApiUsage")
extensions.findByType(KotlinMultiplatformAndroidComponentsExtension::class.java)?.let { androidComponents ->
configureAndroidComposeResources(kotlinExtension, androidComponents, moduleResourceDir)
}
}
}

private fun Project.configureAndroidComposeResources(
kotlinExtension: KotlinMultiplatformExtension,
androidComponents: AndroidComponentsExtension<*, *, *>,
moduleResourceDir: Provider<File>?
) {
logger.info("Configure compose resources with AndroidComponentsExtension")
androidComponents.onVariants { variant ->
configureGeneratedAndroidComponentAssets(variant, moduleResourceDir)
val componentAssets = getAndroidComponentComposeResources(kotlinExtension, variant.name)
configureGeneratedAndroidComponentAssets(
variant.name,
variant.sources,
componentAssets,
moduleResourceDir
)

if (variant is HasAndroidTest) {
variant.androidTest?.let { androidTest ->
configureGeneratedAndroidComponentAssets(androidTest, moduleResourceDir)
val androidTestAssets = getAndroidComponentComposeResources(kotlinExtension, androidTest.name)
configureGeneratedAndroidComponentAssets(
androidTest.name,
androidTest.sources,
androidTestAssets,
moduleResourceDir
)
}
}
}
}

private fun Project.getAndroidComponentComposeResources(
kotlinExtension: KotlinMultiplatformExtension,
componentName: String
): FileCollection = project.files({
kotlinExtension.targets.withType(KotlinAndroidTarget::class.java).flatMap { androidTarget ->
androidTarget.compilations.flatMap { compilation ->
if (compilation.androidVariant.name == componentName) {
compilation.allKotlinSourceSets.map { kotlinSourceSet ->
getPreparedComposeResourcesDir(kotlinSourceSet)
}
} else emptyList()
}
}
})

@Suppress("UnstableApiUsage")
private fun Project.configureAndroidComposeResources(
kotlinExtension: KotlinMultiplatformExtension,
androidComponents: KotlinMultiplatformAndroidComponentsExtension,
moduleResourceDir: Provider<File>?
) {
logger.info("Configure compose resources with KotlinMultiplatformAndroidComponentsExtension")
androidComponents.onVariant { variant ->
val variantAssets = getAndroidKmpComponentComposeResources(kotlinExtension, variant.name)
configureGeneratedAndroidComponentAssets(
variant.name,
variant.sources,
variantAssets,
moduleResourceDir
)

variant.androidTest?.let { androidTest ->
val androidTestAssets = getAndroidKmpComponentComposeResources(kotlinExtension, androidTest.name)
configureGeneratedAndroidComponentAssets(
androidTest.name,
androidTest.sources,
androidTestAssets,
moduleResourceDir
)
}
}
}

@Suppress("UnstableApiUsage")
private fun Project.getAndroidKmpComponentComposeResources(
kotlinExtension: KotlinMultiplatformExtension,
componentName: String
): FileCollection = project.files({
kotlinExtension.targets.withType(KotlinMultiplatformAndroidTarget::class.java).flatMap { androidTarget ->
androidTarget.compilations.flatMap { compilation ->
if (compilation.componentName == componentName) {
compilation.allKotlinSourceSets.map { kotlinSourceSet ->
getPreparedComposeResourcesDir(kotlinSourceSet)
}
} else emptyList()
}
}
})

private fun Project.configureGeneratedAndroidComponentAssets(
component: Component,
componentName: String,
componentSources: Sources,
componentAssets: FileCollection,
moduleResourceDir: Provider<File>?
) {
val kotlinExtension = project.extensions.findByType(KotlinMultiplatformExtension::class.java) ?: return
val camelComponentName = component.name.uppercaseFirstChar()
val componentAssets = getAndroidComponentComposeResources(kotlinExtension, component.name)
logger.info("Configure ${component.name} resources for 'android' target")
logger.info("Configure $componentName resources for 'android' target")

val camelComponentName = componentName.uppercaseFirstChar()
val copyComponentAssets = registerTask<CopyResourcesToAndroidAssetsTask>(
"copy${camelComponentName}ComposeResourcesToAndroidAssets"
) {
from.set(componentAssets)
moduleResourceDir?.let { relativeResourcePlacement.set(it) }
}

component.sources.assets?.addGeneratedSourceDirectory(
componentSources.assets?.addGeneratedSourceDirectory(
copyComponentAssets,
CopyResourcesToAndroidAssetsTask::outputDirectory
)
Expand All @@ -71,21 +166,6 @@ private fun Project.configureGeneratedAndroidComponentAssets(
}
}

private fun Project.getAndroidComponentComposeResources(
kotlinExtension: KotlinMultiplatformExtension,
componentName: String
): FileCollection = project.files({
kotlinExtension.targets.withType(KotlinAndroidTarget::class.java).flatMap { androidTarget ->
androidTarget.compilations.flatMap { compilation ->
if (compilation.androidVariant.name == componentName) {
compilation.allKotlinSourceSets.map { kotlinSourceSet ->
getPreparedComposeResourcesDir(kotlinSourceSet)
}
} else emptyList()
}
}
})

//Copy task doesn't work with 'variant.sources?.assets?.addGeneratedSourceDirectory' API
internal abstract class CopyResourcesToAndroidAssetsTask : DefaultTask() {
@get:Inject
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@ internal const val COMPOSE_RESOURCES_DIR = "composeResources"
internal const val RES_GEN_DIR = "generated/compose/resourceGenerator"
internal const val KMP_RES_EXT = "multiplatformResourcesPublication"
private const val MIN_GRADLE_VERSION_FOR_KMP_RESOURCES = "7.6"
private val androidPluginIds = listOf(
"com.android.application",
"com.android.library"
)
private const val AGP_APP_ID = "com.android.application"
private const val AGP_LIB_ID = "com.android.library"
internal const val AGP_KMP_LIB_ID = "com.android.kotlin.multiplatform.library"

internal fun Project.configureComposeResources(extension: ResourcesExtension) {
val config = provider { extension }
Expand Down Expand Up @@ -64,10 +63,8 @@ internal fun Project.onKotlinJvmApplied(config: Provider<ResourcesExtension>) {
configureJvmOnlyResources(kotlinExtension, config)
}

internal fun Project.onAgpApplied(block: () -> Unit) {
androidPluginIds.forEach { pluginId ->
plugins.withId(pluginId) {
block()
}
internal fun Project.onAgpApplied(block: (pluginId: String) -> Unit) {
listOf(AGP_APP_ID, AGP_LIB_ID, AGP_KMP_LIB_ID).forEach { pluginId ->
plugins.withId(pluginId) { block(pluginId) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ internal fun Project.configureMultimoduleResources(
.all { target -> configureTargetResources(target, moduleIsolationDirectory) }

//configure ANDROID resources
onAgpApplied {
configureAndroidComposeResources(moduleIsolationDirectory)
onAgpApplied { agpId ->
configureAndroidComposeResources(agpId, moduleIsolationDirectory)
fixAndroidLintTaskDependencies()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ internal fun Project.configureSinglemoduleResources(
}
}

onAgpApplied {
configureAndroidComposeResources()
onAgpApplied { agpId ->
configureAndroidComposeResources(agpId)
fixAndroidLintTaskDependencies()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import org.gradle.util.GradleVersion
import org.jetbrains.compose.desktop.ui.tooling.preview.rpc.PreviewLogger
import org.jetbrains.compose.desktop.ui.tooling.preview.rpc.RemoteConnection
import org.jetbrains.compose.desktop.ui.tooling.preview.rpc.receiveConfigFromGradle
import org.jetbrains.compose.internal.Version
import org.jetbrains.compose.test.utils.GradlePluginTestBase
import org.jetbrains.compose.test.utils.checkExists
import org.jetbrains.compose.test.utils.checks
Expand Down Expand Up @@ -77,7 +78,8 @@ class GradlePluginTest : GradlePluginTestBase() {

@Test
fun newAndroidTarget() {
Assumptions.assumeTrue(defaultTestEnvironment.parsedGradleVersion >= GradleVersion.version("8.0.0"))
Assumptions.assumeTrue(defaultTestEnvironment.parsedGradleVersion >= GradleVersion.version("8.10.2"))
Assumptions.assumeTrue(Version.fromString(defaultTestEnvironment.agpVersion) >= Version.fromString("8.8.0-alpha08"))
with(testProject("application/newAndroidTarget")) {
gradle("build", "--dry-run").checks {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package org.jetbrains.compose.test.tests.integration

import org.gradle.util.GradleVersion
import org.jetbrains.compose.desktop.application.internal.ComposeProperties
import org.jetbrains.compose.internal.Version
import org.jetbrains.compose.internal.utils.Arch
import org.jetbrains.compose.internal.utils.OS
import org.jetbrains.compose.internal.utils.currentArch
Expand Down Expand Up @@ -315,6 +316,27 @@ class ResourcesTest : GradlePluginTestBase() {
}
}

@Test
fun testNewAgpResources() {
Assumptions.assumeTrue(defaultTestEnvironment.parsedGradleVersion >= GradleVersion.version("8.10.2"))
Assumptions.assumeTrue(Version.fromString(defaultTestEnvironment.agpVersion) >= Version.fromString("8.8.0-alpha08"))

with(testProject("misc/newAgpResources", defaultTestEnvironment)) {
gradle(":appModule:assembleDebug").checks {
check.logContains("Configure compose resources with KotlinMultiplatformAndroidComponentsExtension")

val resourcesFiles = sequenceOf(
"assets/composeResources/newagpresources.appmodule.generated.resources/values/strings.commonMain.cvr",
"assets/composeResources/newagpresources.featuremodule.generated.resources/values/strings.commonMain.cvr"
)
val apk = file("appModule/build/outputs/apk/debug/appModule-debug.apk")

//isAndroid = false, because the new AGP has an issue with duplicate resources for now
checkResourcesZip(apk, resourcesFiles, false)
}
}
}

@Test
fun testDisableMultimoduleResourcesWithNewKotlin() {
with(testProject("misc/kmpResourcePublication")) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pluginManagement {
id 'org.jetbrains.kotlin.multiplatform' version 'KOTLIN_VERSION_PLACEHOLDER'
id 'org.jetbrains.kotlin.plugin.compose' version 'KOTLIN_VERSION_PLACEHOLDER'
id 'org.jetbrains.compose' version 'COMPOSE_GRADLE_PLUGIN_VERSION_PLACEHOLDER'
id 'com.android.kotlin.multiplatform.library' version '8.2.0-alpha13'
id 'com.android.kotlin.multiplatform.library' version 'AGP_VERSION_PLACEHOLDER'
}
repositories {
mavenLocal()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
plugins {
id("org.jetbrains.compose")
kotlin("multiplatform")
kotlin("plugin.compose")
id("com.android.application")
}

kotlin {
jvmToolchain(11)
androidTarget()
jvm()

sourceSets {
commonMain.dependencies {
implementation(compose.runtime)
implementation(compose.material3)
implementation(compose.components.resources)
implementation(project(":featureModule"))
}

jvmMain.dependencies {
implementation(compose.desktop.currentOs)
}
}
}

android {
namespace = "me.sample.app"
compileSdk = 35
defaultConfig {
applicationId = "org.example.project"
minSdk = 24
targetSdk = 35
versionCode = 1
versionName = "1.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest>
<application/>
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<resources>
<string name="str_1">App text str_1</string>
</resources>
Loading
Loading