Browse Source

Register all hierarchical compose resources in android compilation

k.tskh/font-dup-fix
Konstantin Tskhovrebov 10 months ago
parent
commit
a912c9fe99
  1. 52
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/AndroidTargetConfiguration.kt
  2. 110
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ResourcesGenerator.kt

52
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/AndroidTargetConfiguration.kt

@ -1,52 +0,0 @@
package org.jetbrains.compose.resources
import com.android.build.api.variant.AndroidComponentsExtension
import com.android.build.gradle.BaseExtension
import org.gradle.api.Project
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.*
import org.jetbrains.compose.internal.utils.registerTask
import org.jetbrains.compose.internal.utils.uppercaseFirstChar
import java.io.File
internal fun Project.configureAndroidResources(
commonResourcesDir: Provider<File>,
androidFontsDir: Provider<File>,
onlyIfProvider: Provider<Boolean>
) {
val androidExtension = project.extensions.findByName("android") as? BaseExtension ?: return
val androidComponents = project.extensions.findByType(AndroidComponentsExtension::class.java) ?: return
val androidMainSourceSet = androidExtension.sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME)
androidMainSourceSet.resources.srcDir(commonResourcesDir)
androidMainSourceSet.assets.srcDir(androidFontsDir)
androidComponents.onVariants { variant ->
val copyFonts = registerTask<CopyAndroidAssetsTask>(
"copy${variant.name.uppercaseFirstChar()}FontsToAndroidAssets"
) {
includeEmptyDirs = false
from(commonResourcesDir)
include("**/font*/*")
onlyIf { onlyIfProvider.get() }
}
variant.sources?.assets?.addGeneratedSourceDirectory(
taskProvider = copyFonts,
wiredWith = CopyAndroidAssetsTask::outputDirectory
)
}
}
//https://github.com/JetBrains/compose-multiplatform/issues/4085
private abstract class CopyAndroidAssetsTask : Copy() {
@get:OutputDirectory
abstract val outputDirectory: DirectoryProperty
override fun getDestinationDir(): File =
outputDirectory.get().asFile
override fun setDestinationDir(destination: File) {
outputDirectory.set(destination)
}
}

110
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ResourcesGenerator.kt

@ -1,18 +1,35 @@
package org.jetbrains.compose.resources package org.jetbrains.compose.resources
import com.android.build.api.variant.AndroidComponentsExtension
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.internal.tasks.ProcessJavaResTask
import org.gradle.api.DefaultTask
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.FileSystemOperations
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider import org.gradle.api.provider.Provider
import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.*
import org.jetbrains.compose.ComposePlugin import org.jetbrains.compose.ComposePlugin
import org.jetbrains.compose.desktop.application.internal.ComposeProperties import org.jetbrains.compose.desktop.application.internal.ComposeProperties
import org.jetbrains.compose.internal.KOTLIN_JVM_PLUGIN_ID import org.jetbrains.compose.internal.KOTLIN_JVM_PLUGIN_ID
import org.jetbrains.compose.internal.KOTLIN_MPP_PLUGIN_ID import org.jetbrains.compose.internal.KOTLIN_MPP_PLUGIN_ID
import org.jetbrains.compose.internal.utils.*
import org.jetbrains.compose.internal.utils.dependsOn
import org.jetbrains.compose.internal.utils.registerTask
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
import org.jetbrains.kotlin.gradle.plugin.sources.android.androidSourceSetInfoOrNull
import org.jetbrains.kotlin.gradle.utils.ObservableSet
import java.io.File import java.io.File
import javax.inject.Inject
internal const val COMPOSE_RESOURCES_DIR = "composeResources" internal const val COMPOSE_RESOURCES_DIR = "composeResources"
private const val RES_GEN_DIR = "generated/compose/resourceGenerator" internal const val RES_GEN_DIR = "generated/compose/resourceGenerator"
private val androidPluginIds = listOf( private val androidPluginIds = listOf(
"com.android.application", "com.android.application",
"com.android.library" "com.android.library"
@ -20,27 +37,77 @@ private val androidPluginIds = listOf(
internal fun Project.configureComposeResources() { internal fun Project.configureComposeResources() {
plugins.withId(KOTLIN_MPP_PLUGIN_ID) { plugins.withId(KOTLIN_MPP_PLUGIN_ID) {
configureComposeResources(KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME) val kotlinExtension = project.extensions.getByType(KotlinMultiplatformExtension::class.java)
configureComposeResources(kotlinExtension, KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME)
//when applied AGP then configure android resources
androidPluginIds.forEach { pluginId ->
plugins.withId(pluginId) {
val androidExtension = project.extensions.getByType(BaseExtension::class.java)
configureAndroidComposeResources(kotlinExtension, androidExtension)
}
}
} }
plugins.withId(KOTLIN_JVM_PLUGIN_ID) { plugins.withId(KOTLIN_JVM_PLUGIN_ID) {
configureComposeResources(SourceSet.MAIN_SOURCE_SET_NAME) val kotlinExtension = project.extensions.getByType(KotlinProjectExtension::class.java)
configureComposeResources(kotlinExtension, SourceSet.MAIN_SOURCE_SET_NAME)
} }
} }
private fun Project.configureComposeResources(commonSourceSetName: String) { private fun Project.configureComposeResources(kotlinExtension: KotlinProjectExtension, commonSourceSetName: String) {
val kotlinExtension = project.extensions.getByType(KotlinProjectExtension::class.java)
kotlinExtension.sourceSets.all { sourceSet -> kotlinExtension.sourceSets.all { sourceSet ->
val sourceSetName = sourceSet.name val sourceSetName = sourceSet.name
val composeResourcesPath = project.projectDir.resolve("src/$sourceSetName/$COMPOSE_RESOURCES_DIR") val composeResourcesPath = project.projectDir.resolve("src/$sourceSetName/$COMPOSE_RESOURCES_DIR")
//To compose resources will be packed to a final artefact we need to mark them as resources
//sourceSet.resources works for all targets except ANDROID!
sourceSet.resources.srcDirs(composeResourcesPath) sourceSet.resources.srcDirs(composeResourcesPath)
if (sourceSetName == commonSourceSetName) { if (sourceSetName == commonSourceSetName) {
configureResourceGenerator(composeResourcesPath, sourceSet) configureResourceGenerator(composeResourcesPath, sourceSet)
} }
} }
} }
@OptIn(ExperimentalKotlinGradlePluginApi::class)
private fun Project.configureAndroidComposeResources(
kotlinExtension: KotlinMultiplatformExtension,
androidExtension: BaseExtension
) {
//mark all composeResources as Android resources
kotlinExtension.targets.matching { it is KotlinAndroidTarget }.all { androidTarget ->
androidTarget.compilations.all { compilation: KotlinCompilation<*> ->
compilation.defaultSourceSet.androidSourceSetInfoOrNull?.let { kotlinAndroidSourceSet ->
androidExtension.sourceSets
.matching { it.name == kotlinAndroidSourceSet.androidSourceSetName }
.all { androidSourceSet ->
(compilation.allKotlinSourceSets as ObservableSet<KotlinSourceSet>).forAll { kotlinSourceSet ->
androidSourceSet.resources.srcDir(
projectDir.resolve("src/${kotlinSourceSet.name}/$COMPOSE_RESOURCES_DIR")
)
}
}
}
}
}
//copy fonts from the compose resources dir to android assets
val androidComponents = project.extensions.findByType(AndroidComponentsExtension::class.java) ?: return
val commonResourcesDir = projectDir.resolve("src/${KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME}/$COMPOSE_RESOURCES_DIR")
androidComponents.onVariants { variant ->
val copyFonts = registerTask<CopyAndroidFontsToAssetsTask>(
"copy${variant.name.uppercaseFirstChar()}FontsToAndroidAssets"
) {
from.set(commonResourcesDir)
}
variant.sources?.assets?.addGeneratedSourceDirectory(
taskProvider = copyFonts,
wiredWith = CopyAndroidFontsToAssetsTask::outputDirectory
)
}
}
private fun Project.configureResourceGenerator(commonComposeResourcesDir: File, commonSourceSet: KotlinSourceSet) { private fun Project.configureResourceGenerator(commonComposeResourcesDir: File, commonSourceSet: KotlinSourceSet) {
val commonComposeResources = provider { commonComposeResourcesDir }
val packageName = provider { val packageName = provider {
buildString { buildString {
val group = project.group.toString().lowercase().asUnderscoredIdentifier() val group = project.group.toString().lowercase().asUnderscoredIdentifier()
@ -73,7 +140,7 @@ private fun Project.configureResourceGenerator(commonComposeResourcesDir: File,
) { ) {
it.packageName.set(packageName) it.packageName.set(packageName)
it.shouldGenerateResClass.set(shouldGenerateResClass) it.shouldGenerateResClass.set(shouldGenerateResClass)
it.resDir.set(commonComposeResources) it.resDir.set(commonComposeResourcesDir)
it.codeDir.set(buildDir("$RES_GEN_DIR/kotlin")) it.codeDir.set(buildDir("$RES_GEN_DIR/kotlin"))
} }
@ -86,15 +153,26 @@ private fun Project.configureResourceGenerator(commonComposeResourcesDir: File,
it.dependsOn(genTask) it.dependsOn(genTask)
} }
} }
}
//when applied AGP then configure android resources //Copy task doesn't work with 'variant.sources?.assets?.addGeneratedSourceDirectory' API
androidPluginIds.forEach { pluginId -> internal abstract class CopyAndroidFontsToAssetsTask : DefaultTask() {
plugins.withId(pluginId) { @get:Inject
configureAndroidResources( abstract val fileSystem: FileSystemOperations
commonComposeResources,
buildDir("$RES_GEN_DIR/androidFonts").map { it.asFile }, @get:Input
shouldGenerateResClass abstract val from: Property<File>
)
@get:OutputDirectory
abstract val outputDirectory: DirectoryProperty
@TaskAction
fun action() {
fileSystem.copy {
it.includeEmptyDirs = false
it.from(from)
it.exclude("**/font*/*")
it.into(outputDirectory)
} }
} }
} }
Loading…
Cancel
Save