Skip to content

Commit

Permalink
[gradle] Use addStaticSourceDirectory in AGP to fix AS previews wit…
Browse files Browse the repository at this point in the history
…h compose resources. (#5090)

More info: https://issuetracker.google.com/348208777

Fixes #5053

## Release Notes
### Fixes - Resources
- _(prerelease fix)_ Fix an android app compose resources packaging
broken after introduction AS previews
  • Loading branch information
terrakok authored Jul 10, 2024
1 parent 5e810a0 commit a98b37d
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 106 deletions.
Original file line number Diff line number Diff line change
@@ -1,95 +1,71 @@
package org.jetbrains.compose.resources

import com.android.build.api.AndroidPluginVersion
import com.android.build.api.variant.AndroidComponentsExtension
import com.android.build.api.variant.Variant
import com.android.build.gradle.internal.lint.AndroidLintAnalysisTask
import com.android.build.gradle.internal.lint.LintModelWriterTask
import org.gradle.api.DefaultTask
import org.gradle.api.Project
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.FileCollection
import org.gradle.api.file.FileSystemOperations
import org.gradle.api.provider.Property
import org.gradle.api.tasks.IgnoreEmptyDirectories
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
import org.jetbrains.compose.internal.utils.registerTask
import org.gradle.api.tasks.Copy
import org.jetbrains.compose.internal.utils.dir
import org.jetbrains.compose.internal.utils.uppercaseFirstChar
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmAndroidCompilation
import org.jetbrains.kotlin.gradle.utils.ObservableSet
import javax.inject.Inject

private val agp_8_1_0 = AndroidPluginVersion(8, 1, 0)
internal fun Project.getAndroidVariantComposeResources(
kotlinExtension: KotlinMultiplatformExtension,
variant: Variant
): FileCollection = project.files({
kotlinExtension.targets.withType(KotlinAndroidTarget::class.java).flatMap { androidTarget ->
androidTarget.compilations.flatMap { compilation ->
if (compilation.androidVariant.name == variant.name) {
compilation.allKotlinSourceSets.map { kotlinSourceSet ->
getPreparedComposeResourcesDir(kotlinSourceSet)
}
} else emptyList()
}
}
})

internal fun Project.getKgpAndroidVariantComposeResources(
variant: Variant
): FileCollection = project.files({
val taskName = "${variant.name}AssetsCopyForAGP"
if (tasks.names.contains(taskName)) tasks.named(taskName).map { it.outputs.files }
else files()
})

internal fun Project.configureAndroidComposeResources(
kotlinExtension: KotlinMultiplatformExtension
getVariantComposeResources: (Variant) -> FileCollection
) {
//copy all compose resources to android assets
val androidComponents = project.extensions.findByType(AndroidComponentsExtension::class.java) ?: return
androidComponents.onVariants { variant ->
val variantResources = project.files()

kotlinExtension.targets.withType(KotlinAndroidTarget::class.java).all { androidTarget ->
androidTarget.compilations.all { compilation: KotlinJvmAndroidCompilation ->
if (compilation.androidVariant.name == variant.name) {
project.logger.info("Configure resources for variant ${variant.name}")
(compilation.allKotlinSourceSets as? ObservableSet<KotlinSourceSet>)?.forAll { kotlinSourceSet ->
val preparedComposeResources = getPreparedComposeResourcesDir(kotlinSourceSet)
variantResources.from(preparedComposeResources)
}
}
}
}

val copyResources = registerTask<CopyResourcesToAndroidAssetsTask>(
"copy${variant.name.uppercaseFirstChar()}ResourcesToAndroidAssets"
) {
from.set(variantResources)
val variantAssets = getVariantComposeResources(variant)
val variantAssetsDir = layout.buildDirectory.dir(RES_GEN_DIR).dir(variant.name + "AndroidAssets")

val copyVariantAssets = tasks.register(
"copy${variant.name.uppercaseFirstChar()}ComposeResourcesToAndroidAssets",
Copy::class.java
) { task ->
task.from(variantAssets)
task.into(variantAssetsDir)
}
variant.sources.assets?.apply {
addGeneratedSourceDirectory(
taskProvider = copyResources,
wiredWith = CopyResourcesToAndroidAssetsTask::outputDirectory
)

// https://issuetracker.google.com/348208777
if (androidComponents.pluginVersion >= agp_8_1_0) {
// addGeneratedSourceDirectory doesn't mark the output directory as assets hence AS Compose Preview doesn't work
addStaticSourceDirectory(copyResources.flatMap { it.outputDirectory.asFile }.get().path)
//https://issuetracker.google.com/348208777
variant.sources.assets?.addStaticSourceDirectory(variantAssetsDir.get().asFile.path)
tasks.configureEach { task ->
if (task.name == "merge${variant.name.uppercaseFirstChar()}Assets") {
task.dependsOn(copyVariantAssets)
}

// addGeneratedSourceDirectory doesn't run the copyResources task during AS Compose Preview build
tasks.configureEach { task ->
if (task.name == "compile${variant.name.uppercaseFirstChar()}Sources") {
task.dependsOn(copyResources)
}
//fix task dependencies for AndroidStudio preview
if (task.name == "compile${variant.name.uppercaseFirstChar()}Sources") {
task.dependsOn(copyVariantAssets)
}
//fix linter task dependencies for `build` task
if (task is AndroidLintAnalysisTask || task is LintModelWriterTask) {
task.mustRunAfter(copyVariantAssets)
}
}
}
}

//Copy task doesn't work with 'variant.sources?.assets?.addGeneratedSourceDirectory' API
internal abstract class CopyResourcesToAndroidAssetsTask : DefaultTask() {
@get:Inject
abstract val fileSystem: FileSystemOperations

@get:InputFiles
@get:IgnoreEmptyDirectories
abstract val from: Property<FileCollection>

@get:OutputDirectory
abstract val outputDirectory: DirectoryProperty

@TaskAction
fun action() {
fileSystem.copy {
it.includeEmptyDirs = false
it.from(from)
it.into(outputDirectory)
}
}
}
Expand All @@ -109,38 +85,5 @@ internal fun Project.fixAndroidLintTaskDependencies() {
it is AndroidLintAnalysisTask || it is LintModelWriterTask
}.configureEach {
it.mustRunAfter(tasks.withType(GenerateResourceAccessorsTask::class.java))
it.mustRunAfter(tasks.withType(CopyResourcesToAndroidAssetsTask::class.java))
}
}

// https://issuetracker.google.com/348208777
internal fun Project.configureAndroidAssetsForPreview() {
val androidComponents = project.extensions.findByType(AndroidComponentsExtension::class.java) ?: return
androidComponents.onVariants { variant ->
variant.sources.assets?.apply {
val kgpCopyAssetsTaskName = "${variant.name}AssetsCopyForAGP"

if (androidComponents.pluginVersion >= agp_8_1_0) {
// addGeneratedSourceDirectory doesn't mark the output directory as assets hence AS Compose Preview doesn't work
tasks.configureEach { task ->
if (task.name == kgpCopyAssetsTaskName) {
task.outputs.files.forEach { file ->
addStaticSourceDirectory(file.path)
}
}
}
}

// addGeneratedSourceDirectory doesn't run the copyResources task during AS Compose Preview build
tasks.configureEach { task ->
if (task.name == "compile${variant.name.uppercaseFirstChar()}Sources") {
task.dependsOn(kgpCopyAssetsTaskName)
}
//fix https://github.com/JetBrains/compose-multiplatform/issues/5038
if (task is AndroidLintAnalysisTask || task is LintModelWriterTask) {
task.mustRunAfter(kgpCopyAssetsTaskName)
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,13 @@ private fun Project.onKgpApplied(config: Provider<ResourcesExtension>, kgp: Kotl
if (kmpResourcesAreAvailable) {
configureKmpResources(kotlinExtension, extraProperties.get(KMP_RES_EXT)!!, config)
onAgpApplied {
configureAndroidAssetsForPreview()
//workaround to fix AndroidStudio preview works with compose resources:
//it copies android assets and mark it as a static assets dir
//yes, the same resources will be registered in AGP twice: from KGP and here as static dirs
//but it works fine and there are no problems during merge android assets
configureAndroidComposeResources { variant ->
getKgpAndroidVariantComposeResources(variant)
}
fixAndroidLintTaskDependencies()
}
} else {
Expand All @@ -63,7 +69,9 @@ private fun Project.onKgpApplied(config: Provider<ResourcesExtension>, kgp: Kotl
configureComposeResources(kotlinExtension, commonMain, config)

onAgpApplied {
configureAndroidComposeResources(kotlinExtension)
configureAndroidComposeResources { variant ->
getAndroidVariantComposeResources(kotlinExtension, variant)
}
fixAndroidLintTaskDependencies()
}
}
Expand Down

0 comments on commit a98b37d

Please sign in to comment.