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

Generate functions to find resources by a string ID. #5068

Merged
merged 3 commits 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
Expand Up @@ -6,8 +6,14 @@ import org.jetbrains.compose.ComposePlugin
import org.jetbrains.compose.internal.IDEA_IMPORT_TASK_NAME
import org.jetbrains.compose.internal.IdeaImportTask
import org.jetbrains.compose.internal.utils.uppercaseFirstChar
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinSingleTargetExtension
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.mpp.KotlinMetadataTarget
import org.jetbrains.kotlin.tooling.core.withClosure
import java.io.File

internal fun Project.configureComposeResourcesGeneration(
Expand Down Expand Up @@ -67,6 +73,8 @@ internal fun Project.configureComposeResourcesGeneration(
)
}

configureResourceCollectorsGeneration(kotlinExtension, shouldGenerateCode, packageName, makeAccessorsPublic)

//setup task execution during IDE import
tasks.configureEach { importTask ->
if (importTask.name == IDEA_IMPORT_TASK_NAME) {
Expand Down Expand Up @@ -115,7 +123,7 @@ private fun Project.configureResourceAccessorsGeneration(
logger.info("Configure resource accessors generation for ${sourceSet.name}")

val genTask = tasks.register(
"generateResourceAccessorsFor${sourceSet.name.uppercaseFirstChar()}",
sourceSet.getResourceAccessorsGenerationTaskName(),
GenerateResourceAccessorsTask::class.java
) { task ->
task.packageName.set(packageName)
Expand All @@ -130,6 +138,132 @@ private fun Project.configureResourceAccessorsGeneration(
}
}

//register generated source set
sourceSet.kotlin.srcDir(genTask.map { it.codeDir })
}

private fun KotlinSourceSet.getResourceAccessorsGenerationTaskName(): String {
return "generateResourceAccessorsFor${this.name.uppercaseFirstChar()}"
}

//we have to generate actual resource collector functions for each leaf source set
private fun Project.configureResourceCollectorsGeneration(
kotlinExtension: KotlinProjectExtension,
shouldGenerateCode: Provider<Boolean>,
packageName: Provider<String>,
makeAccessorsPublic: Provider<Boolean>
) {
if (kotlinExtension is KotlinMultiplatformExtension) {
kotlinExtension.sourceSets
.matching { it.name == KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME }
.all { commonMainSourceSet ->
configureExpectResourceCollectorsGeneration(
commonMainSourceSet,
shouldGenerateCode,
packageName,
makeAccessorsPublic
)
}

kotlinExtension.targets.all { target ->
if (target is KotlinAndroidTarget) {
kotlinExtension.sourceSets.matching { it.name == "androidMain" }.all { androidMain ->
configureActualResourceCollectorsGeneration(
androidMain,
shouldGenerateCode,
packageName,
makeAccessorsPublic,
true
)
}
} else if (target !is KotlinMetadataTarget) {
target.compilations.matching { it.name == KotlinCompilation.MAIN_COMPILATION_NAME }.all { compilation ->
configureActualResourceCollectorsGeneration(
compilation.defaultSourceSet,
shouldGenerateCode,
packageName,
makeAccessorsPublic,
true
)
}
}
}
} else if (kotlinExtension is KotlinSingleTargetExtension<*>) {
//JVM only projects
kotlinExtension.target.compilations
.findByName(KotlinCompilation.MAIN_COMPILATION_NAME)
?.let { compilation ->
configureActualResourceCollectorsGeneration(
compilation.defaultSourceSet,
shouldGenerateCode,
packageName,
makeAccessorsPublic,
false
)
}
}

}

private fun Project.configureExpectResourceCollectorsGeneration(
sourceSet: KotlinSourceSet,
shouldGenerateCode: Provider<Boolean>,
packageName: Provider<String>,
makeAccessorsPublic: Provider<Boolean>
) {
logger.info("Configure expect resource collectors generation for ${sourceSet.name}")


val genTask = tasks.register(
"generateExpectResourceCollectorsFor${sourceSet.name.uppercaseFirstChar()}",
GenerateExpectResourceCollectorsTask::class.java
) { task ->
task.packageName.set(packageName)
task.shouldGenerateCode.set(shouldGenerateCode)
task.makeAccessorsPublic.set(makeAccessorsPublic)
task.codeDir.set(layout.buildDirectory.dir("$RES_GEN_DIR/kotlin/${sourceSet.name}ResourceCollectors"))
}

//register generated source set
sourceSet.kotlin.srcDir(genTask.map { it.codeDir })
}

private fun Project.configureActualResourceCollectorsGeneration(
sourceSet: KotlinSourceSet,
shouldGenerateCode: Provider<Boolean>,
packageName: Provider<String>,
makeAccessorsPublic: Provider<Boolean>,
useActualModifier: Boolean
) {
val taskName = "generateActualResourceCollectorsFor${sourceSet.name.uppercaseFirstChar()}"
if (tasks.names.contains(taskName)) {
logger.info("Actual resource collectors generation for ${sourceSet.name} is already configured")
return
}
logger.info("Configure actual resource collectors generation for ${sourceSet.name}")

val accessorDirs = project.files({
val allSourceSets = sourceSet.withClosure { it.dependsOn }
allSourceSets.mapNotNull { item ->
val accessorsTaskName = item.getResourceAccessorsGenerationTaskName()
if (tasks.names.contains(accessorsTaskName)) {
tasks.named(accessorsTaskName, GenerateResourceAccessorsTask::class.java).map { it.codeDir }
} else null
}
})

val genTask = tasks.register(
taskName,
GenerateActualResourceCollectorsTask::class.java
) { task ->
task.packageName.set(packageName)
task.shouldGenerateCode.set(shouldGenerateCode)
task.makeAccessorsPublic.set(makeAccessorsPublic)
task.useActualModifier.set(useActualModifier)
task.resourceAccessorDirs.from(accessorDirs)
task.codeDir.set(layout.buildDirectory.dir("$RES_GEN_DIR/kotlin/${sourceSet.name}ResourceCollectors"))
}

//register generated source set
sourceSet.kotlin.srcDir(genTask.map { it.codeDir })
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package org.jetbrains.compose.resources

import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.PathSensitive
import org.gradle.api.tasks.PathSensitivity
import org.jetbrains.compose.internal.IdeaImportTask
import org.jetbrains.compose.internal.utils.uppercaseFirstChar

internal abstract class GenerateExpectResourceCollectorsTask : IdeaImportTask() {
@get:Input
abstract val packageName: Property<String>

@get:Input
abstract val shouldGenerateCode: Property<Boolean>

@get:Input
abstract val makeAccessorsPublic: Property<Boolean>

@get:OutputDirectory
abstract val codeDir: DirectoryProperty

override fun safeAction() {
val kotlinDir = codeDir.get().asFile

logger.info("Clean directory $kotlinDir")
kotlinDir.deleteRecursively()
kotlinDir.mkdirs()

if (shouldGenerateCode.get()) {
logger.info("Generate expect ResourceCollectors for $kotlinDir")

val pkgName = packageName.get()
val isPublic = makeAccessorsPublic.get()
val spec = getExpectResourceCollectorsFileSpec(pkgName, "ExpectResourceCollectors", isPublic)
spec.writeTo(kotlinDir)
}
}
}

internal abstract class GenerateActualResourceCollectorsTask : IdeaImportTask() {
@get:Input
abstract val packageName: Property<String>

@get:Input
abstract val shouldGenerateCode: Property<Boolean>

@get:Input
abstract val makeAccessorsPublic: Property<Boolean>

@get:Input
abstract val useActualModifier: Property<Boolean>

@get:InputFiles
@get:PathSensitive(PathSensitivity.RELATIVE)
abstract val resourceAccessorDirs: ConfigurableFileCollection

@get:OutputDirectory
abstract val codeDir: DirectoryProperty

override fun safeAction() {
val kotlinDir = codeDir.get().asFile
val inputDirs = resourceAccessorDirs.files

logger.info("Clean directory $kotlinDir")
kotlinDir.deleteRecursively()
kotlinDir.mkdirs()

val inputFiles = inputDirs.flatMap { dir ->
dir.walkTopDown().filter { !it.isHidden && it.isFile && it.extension == "kt" }.toList()
}

if (shouldGenerateCode.get()) {
logger.info("Generate actual ResourceCollectors for $kotlinDir")
val funNames = inputFiles.mapNotNull { inputFile ->
if (inputFile.nameWithoutExtension.contains('.')) {
val (fileName, suffix) = inputFile.nameWithoutExtension.split('.')
val type = ResourceType.values().firstOrNull { fileName.startsWith(it.accessorName, true) }
val name = "_collect${suffix.uppercaseFirstChar()}${fileName}Resources"

if (type == null) {
logger.warn("Unknown resources type: `$inputFile`")
null
} else if (!inputFile.readText().contains(name)) {
logger.warn("A function '$name' is not found in the `$inputFile` file!")
null
} else {
logger.info("Found collector function: `$name`")
type to name
}
} else {
logger.warn("Unknown file name: `$inputFile`")
null
}
}.groupBy({ it.first }, { it.second })

val pkgName = packageName.get()
val isPublic = makeAccessorsPublic.get()
val useActual = useActualModifier.get()
val spec = getActualResourceCollectorsFileSpec(
pkgName,
"ActualResourceCollectors",
isPublic,
useActual,
funNames
)
spec.writeTo(kotlinDir)
} else {
logger.info("Generation ResourceCollectors for $kotlinDir is disabled")
}
}
}
Loading
Loading