diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/AndroidTargetConfiguration.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/AndroidTargetConfiguration.kt deleted file mode 100644 index d251a82b54..0000000000 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/AndroidTargetConfiguration.kt +++ /dev/null @@ -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, - androidFontsDir: Provider, - onlyIfProvider: Provider -) { - 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( - "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) - } -} \ No newline at end of file diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ResourcesGenerator.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ResourcesGenerator.kt index 9378ac67f9..4fa93afdd3 100644 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ResourcesGenerator.kt +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ResourcesGenerator.kt @@ -1,18 +1,35 @@ 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.file.DirectoryProperty +import org.gradle.api.file.FileSystemOperations +import org.gradle.api.provider.Property 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.desktop.application.internal.ComposeProperties import org.jetbrains.compose.internal.KOTLIN_JVM_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.plugin.KotlinCompilation 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 javax.inject.Inject 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( "com.android.application", "com.android.library" @@ -20,27 +37,77 @@ private val androidPluginIds = listOf( internal fun Project.configureComposeResources() { 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) { - 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) { - val kotlinExtension = project.extensions.getByType(KotlinProjectExtension::class.java) +private fun Project.configureComposeResources(kotlinExtension: KotlinProjectExtension, commonSourceSetName: String) { kotlinExtension.sourceSets.all { sourceSet -> val sourceSetName = sourceSet.name 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) + if (sourceSetName == commonSourceSetName) { 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).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( + "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) { - val commonComposeResources = provider { commonComposeResourcesDir } val packageName = provider { buildString { val group = project.group.toString().lowercase().asUnderscoredIdentifier() @@ -73,7 +140,7 @@ private fun Project.configureResourceGenerator(commonComposeResourcesDir: File, ) { it.packageName.set(packageName) it.shouldGenerateResClass.set(shouldGenerateResClass) - it.resDir.set(commonComposeResources) + it.resDir.set(commonComposeResourcesDir) it.codeDir.set(buildDir("$RES_GEN_DIR/kotlin")) } @@ -86,15 +153,26 @@ private fun Project.configureResourceGenerator(commonComposeResourcesDir: File, it.dependsOn(genTask) } } +} - //when applied AGP then configure android resources - androidPluginIds.forEach { pluginId -> - plugins.withId(pluginId) { - configureAndroidResources( - commonComposeResources, - buildDir("$RES_GEN_DIR/androidFonts").map { it.asFile }, - shouldGenerateResClass - ) +//Copy task doesn't work with 'variant.sources?.assets?.addGeneratedSourceDirectory' API +internal abstract class CopyAndroidFontsToAssetsTask : DefaultTask() { + @get:Inject + abstract val fileSystem: FileSystemOperations + + @get:Input + abstract val from: Property + + @get:OutputDirectory + abstract val outputDirectory: DirectoryProperty + + @TaskAction + fun action() { + fileSystem.copy { + it.includeEmptyDirs = false + it.from(from) + it.exclude("**/font*/*") + it.into(outputDirectory) } } -} +} \ No newline at end of file