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] Use addStaticSourceDirectory in AGP to fix AS previews with compose resources. #5090

Merged
merged 1 commit into from
Jul 10, 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
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
Loading