Browse Source

Support source set's hierarchy for compose resources (#4589)

Compose resources can be located in different KMP source sets in the
`composeResources` directory. For each resource an accessor will be
generated in the suitable kotlin source set.
support/1.6.10 v1.6.10-dev1575
Konstantin 4 weeks ago committed by GitHub
parent
commit
062c9eb077
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 45
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/AndroidResources.kt
  2. 59
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ComposeResources.kt
  3. 133
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ComposeResourcesGeneration.kt
  4. 136
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/GenerateResClassTask.kt
  5. 157
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/GenerateResourceAccessorsTask.kt
  6. 50
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/GeneratedResClassSpec.kt
  7. 36
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/KmpResources.kt
  8. 48
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/PrepareComposeResources.kt
  9. 65
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ResClassGeneration.kt
  10. 50
      gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt
  11. 26
      gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected-open-res/androidMainResourceAccessors/my/lib/res/String0.androidMain.kt
  12. 10
      gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected-open-res/commonMainResourceAccessors/my/lib/res/Drawable0.commonMain.kt
  13. 4
      gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected-open-res/commonMainResourceAccessors/my/lib/res/Font0.commonMain.kt
  14. 7
      gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected-open-res/commonMainResourceAccessors/my/lib/res/Plurals0.commonMain.kt
  15. 37
      gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected-open-res/commonMainResourceAccessors/my/lib/res/String0.commonMain.kt
  16. 0
      gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected-open-res/commonResClass/my/lib/res/Res.kt
  17. 26
      gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected-open-res/desktopMainResourceAccessors/my/lib/res/String0.desktopMain.kt
  18. 26
      gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected/androidMainResourceAccessors/app/group/resources_test/generated/resources/String0.androidMain.kt
  19. 10
      gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected/commonMainResourceAccessors/app/group/resources_test/generated/resources/Drawable0.commonMain.kt
  20. 4
      gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected/commonMainResourceAccessors/app/group/resources_test/generated/resources/Font0.commonMain.kt
  21. 7
      gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected/commonMainResourceAccessors/app/group/resources_test/generated/resources/Plurals0.commonMain.kt
  22. 37
      gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected/commonMainResourceAccessors/app/group/resources_test/generated/resources/String0.commonMain.kt
  23. 0
      gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected/commonResClass/app/group/resources_test/generated/resources/Res.kt
  24. 26
      gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected/desktopMainResourceAccessors/app/group/resources_test/generated/resources/String0.desktopMain.kt
  25. 3
      gradle-plugins/compose/src/test/test-projects/misc/commonResources/src/androidMain/composeResources/values/strings.xml
  26. 3
      gradle-plugins/compose/src/test/test-projects/misc/commonResources/src/desktopMain/composeResources/values/desktop_strings.xml
  27. 0
      gradle-plugins/compose/src/test/test-projects/misc/jvmOnlyResources/expected/commonResClass/me/app/jvmonlyresources/generated/resources/Res.kt
  28. 4
      gradle-plugins/compose/src/test/test-projects/misc/jvmOnlyResources/expected/mainResourceAccessors/me/app/jvmonlyresources/generated/resources/Drawable0.main.kt
  29. 3
      gradle-plugins/compose/src/test/test-projects/misc/kmpResourcePublication/appModule/src/androidMain/composeResources/values/android_strings.xml
  30. 10
      gradle-plugins/compose/src/test/test-projects/misc/kmpResourcePublication/appModule/src/androidMain/kotlin/me/sample/app/App.android.kt
  31. 5
      gradle-plugins/compose/src/test/test-projects/misc/kmpResourcePublication/appModule/src/commonMain/kotlin/me/sample/app/App.kt
  32. 3
      gradle-plugins/compose/src/test/test-projects/misc/kmpResourcePublication/appModule/src/iosMain/composeResources/values/ios_strings.xml
  33. 10
      gradle-plugins/compose/src/test/test-projects/misc/kmpResourcePublication/appModule/src/iosMain/kotlin/me/sample/app/App.ios.kt
  34. 3
      gradle-plugins/compose/src/test/test-projects/misc/kmpResourcePublication/appModule/src/jsMain/composeResources/values/js_strings.xml
  35. 10
      gradle-plugins/compose/src/test/test-projects/misc/kmpResourcePublication/appModule/src/jsMain/kotlin/me/sample/app/App.js.kt
  36. 3
      gradle-plugins/compose/src/test/test-projects/misc/kmpResourcePublication/appModule/src/jvmMain/composeResources/values/jvm_strings.xml
  37. 10
      gradle-plugins/compose/src/test/test-projects/misc/kmpResourcePublication/appModule/src/jvmMain/kotlin/me/sample/app/App.jvm.kt
  38. 3
      gradle-plugins/compose/src/test/test-projects/misc/kmpResourcePublication/appModule/src/wasmJsMain/composeResources/values/wasm_strings.xml
  39. 10
      gradle-plugins/compose/src/test/test-projects/misc/kmpResourcePublication/appModule/src/wasmJsMain/kotlin/me/sample/app/App.wasmJs.kt

45
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/AndroidResources.kt

@ -5,9 +5,9 @@ import com.android.build.gradle.BaseExtension
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.provider.Provider
import org.gradle.api.tasks.*
import org.jetbrains.compose.internal.utils.registerTask
import org.jetbrains.compose.internal.utils.uppercaseFirstChar
@ -18,40 +18,31 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmAndroidCompilation
import org.jetbrains.kotlin.gradle.plugin.sources.android.androidSourceSetInfoOrNull
import org.jetbrains.kotlin.gradle.utils.ObservableSet
import java.io.File
import javax.inject.Inject
@OptIn(ExperimentalKotlinGradlePluginApi::class)
internal fun Project.configureAndroidComposeResources(
kotlinExtension: KotlinMultiplatformExtension,
androidExtension: BaseExtension,
preparedCommonResources: Provider<File>
androidExtension: BaseExtension
) {
val commonMain = KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME
val commonResourcesDir = projectDir.resolve("src/$commonMain/$COMPOSE_RESOURCES_DIR")
// 1) get the Kotlin Android Target Compilation -> [A]
// 2) get default source set name for the 'A'
// 3) find the associated Android SourceSet in the AndroidExtension -> [B]
// 4) get all source sets in the 'A' and add its resources to the 'B'
kotlinExtension.targets.withType(KotlinAndroidTarget::class.java).all { androidTarget ->
androidTarget.compilations.all { compilation: KotlinJvmAndroidCompilation ->
//fix for AGP < 8.0
//usually 'androidSourceSet.resources.srcDir(preparedCommonResources)' should be enough
compilation.androidVariant.processJavaResourcesProvider.configure { it.dependsOn(preparedCommonResources) }
compilation.defaultSourceSet.androidSourceSetInfoOrNull?.let { kotlinAndroidSourceSet ->
androidExtension.sourceSets
.matching { it.name == kotlinAndroidSourceSet.androidSourceSetName }
.all { androidSourceSet ->
(compilation.allKotlinSourceSets as? ObservableSet<KotlinSourceSet>)?.forAll { kotlinSourceSet ->
if (kotlinSourceSet.name == commonMain) {
androidSourceSet.resources.srcDir(preparedCommonResources)
} else {
androidSourceSet.resources.srcDir(
projectDir.resolve("src/${kotlinSourceSet.name}/$COMPOSE_RESOURCES_DIR")
)
val preparedComposeResources = getPreparedComposeResourcesDir(kotlinSourceSet)
androidSourceSet.resources.srcDirs(preparedComposeResources)
//fix for AGP < 8.0
//usually 'androidSourceSet.resources.srcDir(preparedCommonResources)' should be enough
compilation.androidVariant.processJavaResourcesProvider.configure {
it.dependsOn(preparedComposeResources)
}
}
}
@ -62,10 +53,24 @@ internal fun Project.configureAndroidComposeResources(
//copy fonts from the compose resources dir 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 fonts for variant ${variant.name}")
(compilation.allKotlinSourceSets as? ObservableSet<KotlinSourceSet>)?.forAll { kotlinSourceSet ->
val preparedComposeResources = getPreparedComposeResourcesDir(kotlinSourceSet)
variantResources.from(preparedComposeResources)
}
}
}
}
val copyFonts = registerTask<CopyAndroidFontsToAssetsTask>(
"copy${variant.name.uppercaseFirstChar()}FontsToAndroidAssets"
) {
from.set(commonResourcesDir)
from.set(variantResources)
}
variant.sources?.assets?.addGeneratedSourceDirectory(
taskProvider = copyFonts,
@ -83,7 +88,7 @@ internal abstract class CopyAndroidFontsToAssetsTask : DefaultTask() {
@get:InputFiles
@get:IgnoreEmptyDirectories
abstract val from: Property<File>
abstract val from: Property<FileCollection>
@get:OutputDirectory
abstract val outputDirectory: DirectoryProperty

59
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ComposeResources.kt

@ -1,11 +1,14 @@
package org.jetbrains.compose.resources
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.internal.lint.AndroidLintAnalysisTask
import com.android.build.gradle.internal.lint.LintModelWriterTask
import org.gradle.api.Project
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.SourceSet
import org.gradle.api.tasks.TaskProvider
import org.gradle.util.GradleVersion
import org.jetbrains.compose.ComposePlugin
import org.jetbrains.compose.internal.KOTLIN_JVM_PLUGIN_ID
import org.jetbrains.compose.internal.KOTLIN_MPP_PLUGIN_ID
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
@ -33,22 +36,18 @@ internal fun Project.configureComposeResources(extension: ResourcesExtension) {
private fun Project.onKgpApplied(config: Provider<ResourcesExtension>) {
val kotlinExtension = project.extensions.getByType(KotlinMultiplatformExtension::class.java)
//common resources must be converted (XML -> CVR)
val commonMain = KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME
val preparedCommonResources = prepareCommonResources(commonMain)
val hasKmpResources = extraProperties.has(KMP_RES_EXT)
val currentGradleVersion = GradleVersion.current()
val minGradleVersion = GradleVersion.version(MIN_GRADLE_VERSION_FOR_KMP_RESOURCES)
val kmpResourcesAreAvailable = hasKmpResources && currentGradleVersion >= minGradleVersion
if (kmpResourcesAreAvailable) {
configureKmpResources(kotlinExtension, extraProperties.get(KMP_RES_EXT)!!, preparedCommonResources, config)
configureKmpResources(kotlinExtension, extraProperties.get(KMP_RES_EXT)!!, config)
} else {
if (!hasKmpResources) logger.info(
"""
Compose resources publication requires Kotlin Gradle Plugin >= 2.0
Current Kotlin Gradle Plugin is ${KotlinVersion.CURRENT}
Current Kotlin Gradle Plugin is ${kotlinExtension.coreLibrariesVersion}
""".trimIndent()
)
if (currentGradleVersion < minGradleVersion) logger.info(
@ -58,13 +57,31 @@ private fun Project.onKgpApplied(config: Provider<ResourcesExtension>) {
""".trimIndent()
)
configureComposeResources(kotlinExtension, commonMain, preparedCommonResources, config)
val commonMain = KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME
configureComposeResources(kotlinExtension, commonMain, config)
//when applied AGP then configure android resources
androidPluginIds.forEach { pluginId ->
plugins.withId(pluginId) {
val androidExtension = project.extensions.getByType(BaseExtension::class.java)
configureAndroidComposeResources(kotlinExtension, androidExtension, preparedCommonResources)
configureAndroidComposeResources(kotlinExtension, androidExtension)
/*
There is a dirty fix for the problem:
Reason: Task ':generateDemoDebugUnitTestLintModel' uses this output of task ':generateResourceAccessorsForAndroidUnitTest' without declaring an explicit or implicit dependency. This can lead to incorrect results being produced, depending on what order the tasks are executed.
Possible solutions:
1. Declare task ':generateResourceAccessorsForAndroidUnitTest' as an input of ':generateDemoDebugUnitTestLintModel'.
2. Declare an explicit dependency on ':generateResourceAccessorsForAndroidUnitTest' from ':generateDemoDebugUnitTestLintModel' using Task#dependsOn.
3. Declare an explicit dependency on ':generateResourceAccessorsForAndroidUnitTest' from ':generateDemoDebugUnitTestLintModel' using Task#mustRunAfter.
*/
tasks.matching {
it is AndroidLintAnalysisTask || it is LintModelWriterTask
}.configureEach {
it.mustRunAfter(tasks.withType(GenerateResourceAccessorsTask::class.java))
}
}
}
}
@ -75,36 +92,20 @@ private fun Project.onKgpApplied(config: Provider<ResourcesExtension>) {
private fun Project.onKotlinJvmApplied(config: Provider<ResourcesExtension>) {
val kotlinExtension = project.extensions.getByType(KotlinProjectExtension::class.java)
val main = SourceSet.MAIN_SOURCE_SET_NAME
val preparedCommonResources = prepareCommonResources(main)
configureComposeResources(kotlinExtension, main, preparedCommonResources, config)
}
//common resources must be converted (XML -> CVR)
private fun Project.prepareCommonResources(commonSourceSetName: String): Provider<File> {
val preparedResourcesTask = registerPrepareComposeResourcesTask(
project.projectDir.resolve("src/$commonSourceSetName/$COMPOSE_RESOURCES_DIR"),
layout.buildDirectory.dir("$RES_GEN_DIR/preparedResources/$commonSourceSetName/$COMPOSE_RESOURCES_DIR")
)
return preparedResourcesTask.flatMap { it.outputDir }
configureComposeResources(kotlinExtension, main, config)
}
// sourceSet.resources.srcDirs doesn't work for Android targets.
// Android resources should be configured separately
private fun Project.configureComposeResources(
kotlinExtension: KotlinProjectExtension,
commonSourceSetName: String,
preparedCommonResources: Provider<File>,
resClassSourceSetName: String,
config: Provider<ResourcesExtension>
) {
logger.info("Configure compose resources")
configureComposeResourcesGeneration(kotlinExtension, resClassSourceSetName, config, false)
kotlinExtension.sourceSets.all { sourceSet ->
val sourceSetName = sourceSet.name
val resourcesDir = project.projectDir.resolve("src/$sourceSetName/$COMPOSE_RESOURCES_DIR")
if (sourceSetName == commonSourceSetName) {
sourceSet.resources.srcDirs(preparedCommonResources)
configureGenerationComposeResClass(preparedCommonResources, sourceSet, config, false)
} else {
sourceSet.resources.srcDirs(resourcesDir)
}
sourceSet.resources.srcDirs(getPreparedComposeResourcesDir(sourceSet))
}
}

133
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ComposeResourcesGeneration.kt

@ -0,0 +1,133 @@
package org.jetbrains.compose.resources
import org.gradle.api.Project
import org.gradle.api.provider.Provider
import org.jetbrains.compose.ComposePlugin
import org.jetbrains.compose.internal.utils.uppercaseFirstChar
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import java.io.File
internal fun Project.configureComposeResourcesGeneration(
kotlinExtension: KotlinProjectExtension,
resClassSourceSetName: String,
config: Provider<ResourcesExtension>,
generateModulePath: Boolean
) {
logger.info("Configure compose resources generation")
//lazy check a dependency on the Resources library
val shouldGenerateCode = config.map {
when (it.generateResClass) {
ResourcesExtension.ResourceClassGeneration.Auto -> {
configurations.run {
val commonSourceSet = kotlinExtension.sourceSets.getByName(resClassSourceSetName)
//because the implementation configuration doesn't extend the api in the KGP ¯\_(ツ)_/¯
getByName(commonSourceSet.implementationConfigurationName).allDependencies +
getByName(commonSourceSet.apiConfigurationName).allDependencies
}.any { dep ->
val depStringNotation = dep.let { "${it.group}:${it.name}:${it.version}" }
depStringNotation == ComposePlugin.CommonComponentsDependencies.resources
}
}
ResourcesExtension.ResourceClassGeneration.Always -> true
ResourcesExtension.ResourceClassGeneration.Never -> false
}
}
val packageName = config.getResourcePackage(project)
val makeAccessorsPublic = config.map { it.publicResClass }
val packagingDir = config.getModuleResourcesDir(project)
kotlinExtension.sourceSets.all { sourceSet ->
if (sourceSet.name == resClassSourceSetName) {
configureResClassGeneration(
sourceSet,
shouldGenerateCode,
packageName,
makeAccessorsPublic,
packagingDir,
generateModulePath
)
}
//common resources must be converted (XML -> CVR)
val preparedResourcesTask = registerPrepareComposeResourcesTask(sourceSet)
val preparedResources = preparedResourcesTask.flatMap { it.outputDir.asFile }
configureResourceAccessorsGeneration(
sourceSet,
preparedResources,
shouldGenerateCode,
packageName,
makeAccessorsPublic,
packagingDir,
generateModulePath
)
}
//setup task execution during IDE import
tasks.configureEach { importTask ->
if (importTask.name == "prepareKotlinIdeaImport") {
importTask.dependsOn(tasks.withType(CodeGenerationTask::class.java))
}
}
}
private fun Project.configureResClassGeneration(
resClassSourceSet: KotlinSourceSet,
shouldGenerateCode: Provider<Boolean>,
packageName: Provider<String>,
makeAccessorsPublic: Provider<Boolean>,
packagingDir: Provider<File>,
generateModulePath: Boolean
) {
logger.info("Configure Res class generation for ${resClassSourceSet.name}")
val genTask = tasks.register(
"generateComposeResClass",
GenerateResClassTask::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/commonResClass"))
if (generateModulePath) {
task.packagingDir.set(packagingDir)
}
}
//register generated source set
resClassSourceSet.kotlin.srcDir(genTask.map { it.codeDir })
}
private fun Project.configureResourceAccessorsGeneration(
sourceSet: KotlinSourceSet,
resourcesDir: Provider<File>,
shouldGenerateCode: Provider<Boolean>,
packageName: Provider<String>,
makeAccessorsPublic: Provider<Boolean>,
packagingDir: Provider<File>,
generateModulePath: Boolean
) {
logger.info("Configure resource accessors generation for ${sourceSet.name}")
val genTask = tasks.register(
"generateResourceAccessorsFor${sourceSet.name.uppercaseFirstChar()}",
GenerateResourceAccessorsTask::class.java
) { task ->
task.packageName.set(packageName)
task.sourceSetName.set(sourceSet.name)
task.shouldGenerateCode.set(shouldGenerateCode)
task.makeAccessorsPublic.set(makeAccessorsPublic)
task.resDir.set(resourcesDir)
task.codeDir.set(layout.buildDirectory.dir("$RES_GEN_DIR/kotlin/${sourceSet.name}ResourceAccessors"))
if (generateModulePath) {
task.packagingDir.set(packagingDir)
}
}
//register generated source set
sourceSet.kotlin.srcDir(genTask.map { it.codeDir })
}

136
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/GenerateResClassTask.kt

@ -1,147 +1,59 @@
package org.jetbrains.compose.resources
import org.gradle.api.DefaultTask
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFile
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.*
import java.io.File
import java.io.RandomAccessFile
import java.nio.file.Path
import kotlin.io.path.relativeTo
/**
* This task should be FAST and SAFE! Because it is being run during IDE import.
*/
internal abstract class GenerateResClassTask : DefaultTask() {
internal abstract class CodeGenerationTask : DefaultTask()
internal abstract class GenerateResClassTask : CodeGenerationTask() {
companion object {
private const val RES_FILE_NAME = "Res"
}
@get:Input
abstract val packageName: Property<String>
@get:Input
@get:Optional
abstract val moduleDir: Property<File>
abstract val packagingDir: Property<File>
@get:Input
abstract val shouldGenerateResClass: Property<Boolean>
abstract val shouldGenerateCode: Property<Boolean>
@get:Input
abstract val makeResClassPublic: Property<Boolean>
@get:InputFiles
@get:PathSensitive(PathSensitivity.RELATIVE)
abstract val resDir: Property<File>
abstract val makeAccessorsPublic: Property<Boolean>
@get:OutputDirectory
abstract val codeDir: Property<File>
abstract val codeDir: DirectoryProperty
@TaskAction
fun generate() {
try {
val kotlinDir = codeDir.get()
logger.info("Clean directory $kotlinDir")
kotlinDir.deleteRecursively()
kotlinDir.mkdirs()
val dir = codeDir.get().asFile
dir.deleteRecursively()
dir.mkdirs()
if (shouldGenerateResClass.get()) {
val rootResDir = resDir.get()
logger.info("Generate resources for $rootResDir")
if (shouldGenerateCode.get()) {
logger.info("Generate $RES_FILE_NAME.kt")
//get first level dirs
val dirs = rootResDir.listNotHiddenFiles()
dirs.forEach { f ->
if (!f.isDirectory) {
error("${f.name} is not directory! Raw files should be placed in '${rootResDir.name}/files' directory.")
}
}
//type -> id -> resource item
val resources: Map<ResourceType, Map<String, List<ResourceItem>>> = dirs
.flatMap { dir ->
dir.listNotHiddenFiles()
.mapNotNull { it.fileToResourceItems(rootResDir.toPath()) }
.flatten()
}
.groupBy { it.type }
.mapValues { (_, items) -> items.groupBy { it.name } }
getResFileSpecs(
resources,
packageName.get(),
moduleDir.getOrNull()?.let { it.invariantSeparatorsPath + "/" } ?: "",
makeResClassPublic.get()
).forEach { it.writeTo(kotlinDir) }
val pkgName = packageName.get()
val moduleDirectory = packagingDir.getOrNull()?.let { it.invariantSeparatorsPath + "/" } ?: ""
val isPublic = makeAccessorsPublic.get()
getResFileSpec(pkgName, RES_FILE_NAME, moduleDirectory, isPublic).writeTo(dir)
} else {
logger.info("Generation Res class is disabled")
}
} catch (e: Exception) {
//message must contain two ':' symbols to be parsed by IDE UI!
logger.error("e: GenerateResClassTask was failed:", e)
logger.error("e: $name task was failed:", e)
}
}
private fun File.fileToResourceItems(
relativeTo: Path
): List<ResourceItem>? {
val file = this
val dirName = file.parentFile.name ?: return null
val typeAndQualifiers = dirName.split("-")
if (typeAndQualifiers.isEmpty()) return null
val typeString = typeAndQualifiers.first().lowercase()
val qualifiers = typeAndQualifiers.takeLast(typeAndQualifiers.size - 1)
val path = file.toPath().relativeTo(relativeTo)
if (typeString == "string") {
error("Forbidden directory name '$dirName'! String resources should be declared in 'values/strings.xml'.")
}
if (typeString == "files") {
if (qualifiers.isNotEmpty()) error("The 'files' directory doesn't support qualifiers: '$dirName'.")
return null
}
if (typeString == "values" && file.extension.equals(XmlValuesConverterTask.CONVERTED_RESOURCE_EXT, true)) {
return getValueResourceItems(file, qualifiers, path)
}
val type = ResourceType.fromString(typeString) ?: error("Unknown resource type: '$typeString'.")
return listOf(ResourceItem(type, qualifiers, file.nameWithoutExtension.asUnderscoredIdentifier(), path))
}
private fun getValueResourceItems(dataFile: File, qualifiers: List<String>, path: Path) : List<ResourceItem> {
val result = mutableListOf<ResourceItem>()
dataFile.bufferedReader().use { f ->
var offset = 0L
var line: String? = f.readLine()
while (line != null) {
val size = line.encodeToByteArray().size
//first line is meta info
if (offset > 0) {
result.add(getValueResourceItem(line, offset, size.toLong(), qualifiers, path))
}
offset += size + 1 // "+1" for newline character
line = f.readLine()
}
}
return result
}
private fun getValueResourceItem(
recordString: String,
offset: Long,
size: Long,
qualifiers: List<String>,
path: Path
) : ResourceItem {
val record = ValueResourceRecord.createFromString(recordString)
return ResourceItem(record.type, qualifiers, record.key.asUnderscoredIdentifier(), path, offset, size)
}
}
internal fun File.listNotHiddenFiles(): List<File> =
listFiles()?.filter { !it.isHidden }.orEmpty()
internal fun String.asUnderscoredIdentifier(): String =
replace('-', '_')
.let { if (it.isNotEmpty() && it.first().isDigit()) "_$it" else it }

157
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/GenerateResourceAccessorsTask.kt

@ -0,0 +1,157 @@
package org.jetbrains.compose.resources
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.Optional
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.PathSensitive
import org.gradle.api.tasks.PathSensitivity
import org.gradle.api.tasks.SkipWhenEmpty
import org.gradle.api.tasks.TaskAction
import java.io.File
import java.nio.file.Path
import kotlin.io.path.relativeTo
internal abstract class GenerateResourceAccessorsTask : CodeGenerationTask() {
@get:Input
abstract val packageName: Property<String>
@get:Input
abstract val sourceSetName: Property<String>
@get:Input
@get:Optional
abstract val packagingDir: Property<File>
@get:Input
abstract val shouldGenerateCode: Property<Boolean>
@get:Input
abstract val makeAccessorsPublic: Property<Boolean>
@get:InputFiles
@get:SkipWhenEmpty
@get:PathSensitive(PathSensitivity.RELATIVE)
abstract val resDir: Property<File>
@get:OutputDirectory
abstract val codeDir: DirectoryProperty
@TaskAction
fun generate() {
try {
val kotlinDir = codeDir.get().asFile
val rootResDir = resDir.get()
val sourceSet = sourceSetName.get()
logger.info("Clean directory $kotlinDir")
kotlinDir.deleteRecursively()
kotlinDir.mkdirs()
if (shouldGenerateCode.get()) {
logger.info("Generate accessors for $rootResDir")
//get first level dirs
val dirs = rootResDir.listNotHiddenFiles()
dirs.forEach { f ->
if (!f.isDirectory) {
error("${f.name} is not directory! Raw files should be placed in '${rootResDir.name}/files' directory.")
}
}
//type -> id -> resource item
val resources: Map<ResourceType, Map<String, List<ResourceItem>>> = dirs
.flatMap { dir ->
dir.listNotHiddenFiles()
.mapNotNull { it.fileToResourceItems(rootResDir.toPath()) }
.flatten()
}
.groupBy { it.type }
.mapValues { (_, items) -> items.groupBy { it.name } }
val pkgName = packageName.get()
val moduleDirectory = packagingDir.getOrNull()?.let { it.invariantSeparatorsPath + "/" } ?: ""
val isPublic = makeAccessorsPublic.get()
getAccessorsSpecs(
resources, pkgName, sourceSet, moduleDirectory, isPublic
).forEach { it.writeTo(kotlinDir) }
} else {
logger.info("Generation accessors for $rootResDir is disabled")
}
} catch (e: Exception) {
//message must contain two ':' symbols to be parsed by IDE UI!
logger.error("e: $name task was failed:", e)
}
}
private fun File.fileToResourceItems(
relativeTo: Path
): List<ResourceItem>? {
val file = this
val dirName = file.parentFile.name ?: return null
val typeAndQualifiers = dirName.split("-")
if (typeAndQualifiers.isEmpty()) return null
val typeString = typeAndQualifiers.first().lowercase()
val qualifiers = typeAndQualifiers.takeLast(typeAndQualifiers.size - 1)
val path = file.toPath().relativeTo(relativeTo)
if (typeString == "string") {
error("Forbidden directory name '$dirName'! String resources should be declared in 'values/strings.xml'.")
}
if (typeString == "files") {
if (qualifiers.isNotEmpty()) error("The 'files' directory doesn't support qualifiers: '$dirName'.")
return null
}
if (typeString == "values" && file.extension.equals(XmlValuesConverterTask.CONVERTED_RESOURCE_EXT, true)) {
return getValueResourceItems(file, qualifiers, path)
}
val type = ResourceType.fromString(typeString) ?: error("Unknown resource type: '$typeString'.")
return listOf(ResourceItem(type, qualifiers, file.nameWithoutExtension.asUnderscoredIdentifier(), path))
}
private fun getValueResourceItems(dataFile: File, qualifiers: List<String>, path: Path): List<ResourceItem> {
val result = mutableListOf<ResourceItem>()
dataFile.bufferedReader().use { f ->
var offset = 0L
var line: String? = f.readLine()
while (line != null) {
val size = line.encodeToByteArray().size
//first line is meta info
if (offset > 0) {
result.add(getValueResourceItem(line, offset, size.toLong(), qualifiers, path))
}
offset += size + 1 // "+1" for newline character
line = f.readLine()
}
}
return result
}
private fun getValueResourceItem(
recordString: String,
offset: Long,
size: Long,
qualifiers: List<String>,
path: Path
): ResourceItem {
val record = ValueResourceRecord.createFromString(recordString)
return ResourceItem(record.type, qualifiers, record.key.asUnderscoredIdentifier(), path, offset, size)
}
}
internal fun File.listNotHiddenFiles(): List<File> =
listFiles()?.filter { !it.isHidden }.orEmpty()
internal fun String.asUnderscoredIdentifier(): String =
replace('-', '_')
.let { if (it.isNotEmpty() && it.first().isDigit()) "_$it" else it }

50
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/GeneratedResClassSpec.kt

@ -111,25 +111,14 @@ private fun CodeBlock.Builder.addQualifiers(resourceItem: ResourceItem): CodeBlo
return this
}
// We need to divide accessors by different files because
//
// if all accessors are generated in a single object
// then a build may fail with: org.jetbrains.org.objectweb.asm.MethodTooLargeException: Method too large: Res$drawable.<clinit> ()V
// e.g. https://github.com/JetBrains/compose-multiplatform/issues/4285
//
// if accessor initializers are extracted from the single object but located in the same file
// then a build may fail with: org.jetbrains.org.objectweb.asm.ClassTooLargeException: Class too large: Res$drawable
private const val ITEMS_PER_FILE_LIMIT = 500
internal fun getResFileSpecs(
//type -> id -> items
resources: Map<ResourceType, Map<String, List<ResourceItem>>>,
internal fun getResFileSpec(
packageName: String,
fileName: String,
moduleDir: String,
isPublic: Boolean
): List<FileSpec> {
): FileSpec {
val resModifier = if (isPublic) KModifier.PUBLIC else KModifier.INTERNAL
val files = mutableListOf<FileSpec>()
val resClass = FileSpec.builder(packageName, "Res").also { file ->
return FileSpec.builder(packageName, fileName).also { file ->
file.addAnnotation(
AnnotationSpec.builder(ClassName("kotlin", "OptIn"))
.addMember("org.jetbrains.compose.resources.InternalResourceApi::class")
@ -186,7 +175,27 @@ internal fun getResFileSpecs(
}
}.build())
}.build()
files.add(resClass)
}
// We need to divide accessors by different files because
//
// if all accessors are generated in a single object
// then a build may fail with: org.jetbrains.org.objectweb.asm.MethodTooLargeException: Method too large: Res$drawable.<clinit> ()V
// e.g. https://github.com/JetBrains/compose-multiplatform/issues/4285
//
// if accessor initializers are extracted from the single object but located in the same file
// then a build may fail with: org.jetbrains.org.objectweb.asm.ClassTooLargeException: Class too large: Res$drawable
private const val ITEMS_PER_FILE_LIMIT = 500
internal fun getAccessorsSpecs(
//type -> id -> items
resources: Map<ResourceType, Map<String, List<ResourceItem>>>,
packageName: String,
sourceSetName: String,
moduleDir: String,
isPublic: Boolean
): List<FileSpec> {
val resModifier = if (isPublic) KModifier.PUBLIC else KModifier.INTERNAL
val files = mutableListOf<FileSpec>()
//we need to sort it to generate the same code on different platforms
sortResources(resources).forEach { (type, idToResources) ->
@ -196,7 +205,8 @@ internal fun getResFileSpecs(
files.add(
getChunkFileSpec(
type,
index,
"${type.accessorName.uppercaseFirstChar()}$index.$sourceSetName",
sourceSetName.uppercaseFirstChar() + type.accessorName.uppercaseFirstChar() + index,
packageName,
moduleDir,
resModifier,
@ -211,14 +221,14 @@ internal fun getResFileSpecs(
private fun getChunkFileSpec(
type: ResourceType,
index: Int,
fileName: String,
chunkClassName: String,
packageName: String,
moduleDir: String,
resModifier: KModifier,
idToResources: Map<String, List<ResourceItem>>
): FileSpec {
val chunkClassName = type.accessorName.uppercaseFirstChar() + index
return FileSpec.builder(packageName, chunkClassName).also { chunkFile ->
return FileSpec.builder(packageName, fileName).also { chunkFile ->
chunkFile.addAnnotation(
AnnotationSpec.builder(ClassName("kotlin", "OptIn"))
.addMember("org.jetbrains.compose.resources.InternalResourceApi::class")

36
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/KmpResources.kt

@ -15,7 +15,6 @@ import java.io.File
internal fun Project.configureKmpResources(
kotlinExtension: KotlinProjectExtension,
kmpResources: Any,
preparedCommonResources: Provider<File>,
config: Provider<ResourcesExtension>
) {
kotlinExtension as KotlinMultiplatformExtension
@ -23,6 +22,9 @@ internal fun Project.configureKmpResources(
logger.info("Configure KMP resources")
val commonMain = KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME
configureComposeResourcesGeneration(kotlinExtension, commonMain, config, true)
//configure KMP resources publishing for each supported target
kotlinExtension.targets
.matching { target -> kmpResources.canPublishResources(target) }
@ -33,16 +35,8 @@ internal fun Project.configureKmpResources(
kmpResources.publishResourcesAsKotlinComponent(
target,
{ sourceSet ->
val sourceSetName = sourceSet.name
val composeResDir: Provider<File>
if (sourceSetName == KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME) {
composeResDir = preparedCommonResources
} else {
composeResDir = provider { project.file("src/$sourceSetName/$COMPOSE_RESOURCES_DIR") }
}
KotlinTargetResourcesPublication.ResourceRoot(
composeResDir,
getPreparedComposeResourcesDir(sourceSet),
emptyList(),
//for android target exclude fonts
if (target is KotlinAndroidTarget) listOf("**/font*/*") else emptyList()
@ -57,15 +51,8 @@ internal fun Project.configureKmpResources(
kmpResources.publishInAndroidAssets(
target,
{ sourceSet ->
val sourceSetName = sourceSet.name
val composeResDir: Provider<File>
if (sourceSetName == KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME) {
composeResDir = preparedCommonResources
} else {
composeResDir = provider { project.file("src/$sourceSetName/$COMPOSE_RESOURCES_DIR") }
}
KotlinTargetResourcesPublication.ResourceRoot(
composeResDir,
getPreparedComposeResourcesDir(sourceSet),
listOf("**/font*/*"),
emptyList()
)
@ -75,19 +62,6 @@ internal fun Project.configureKmpResources(
}
}
//generate accessors for common resources
kotlinExtension.sourceSets.all { sourceSet ->
val sourceSetName = sourceSet.name
if (sourceSetName == KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME) {
configureGenerationComposeResClass(
preparedCommonResources,
sourceSet,
config,
true
)
}
}
//add all resolved resources for browser and native compilations
val platformsForSetupCompilation = listOf(KotlinPlatformType.native, KotlinPlatformType.js, KotlinPlatformType.wasm)
kotlinExtension.targets

48
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/PrepareComposeResources.kt

@ -6,6 +6,8 @@ import org.gradle.api.file.*
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.*
import org.jetbrains.compose.internal.utils.uppercaseFirstChar
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.w3c.dom.Node
import java.io.File
import java.util.*
@ -13,19 +15,23 @@ import javax.inject.Inject
import javax.xml.parsers.DocumentBuilderFactory
internal fun Project.registerPrepareComposeResourcesTask(
userComposeResourcesDir: File,
preparedComposeResourcesDir: Provider<Directory>
sourceSet: KotlinSourceSet
): TaskProvider<PrepareComposeResourcesTask> {
val resDir = "${sourceSet.name}/$COMPOSE_RESOURCES_DIR"
val userComposeResourcesDir = project.projectDir.resolve("src/$resDir")
val preparedComposeResourcesDir = layout.buildDirectory.dir("$RES_GEN_DIR/preparedResources/$resDir")
val convertXmlValueResources = tasks.register(
"convertXmlValueResources",
"convertXmlValueResourcesFor${sourceSet.name.uppercaseFirstChar()}",
XmlValuesConverterTask::class.java
) { task ->
task.fileSuffix.set(sourceSet.name)
task.originalResourcesDir.set(userComposeResourcesDir)
task.outputDir.set(preparedComposeResourcesDir)
}
val copyNonXmlValueResources = tasks.register(
"copyNonXmlValueResources",
"copyNonXmlValueResourcesFor${sourceSet.name.uppercaseFirstChar()}",
CopyNonXmlValueResourcesTask::class.java
) { task ->
task.originalResourcesDir.set(userComposeResourcesDir)
@ -33,17 +39,27 @@ internal fun Project.registerPrepareComposeResourcesTask(
}
val prepareComposeResourcesTask = tasks.register(
"prepareComposeResourcesTask",
getPrepareComposeResourcesTaskName(sourceSet),
PrepareComposeResourcesTask::class.java
) { task ->
task.convertedXmls.set(convertXmlValueResources.map { it.realOutputFiles.get() })
task.copiedNonXmls.set(copyNonXmlValueResources.map { it.realOutputFiles.get() })
task.outputDir.set(preparedComposeResourcesDir.map { it.asFile })
task.outputDir.set(preparedComposeResourcesDir)
}
return prepareComposeResourcesTask
}
internal fun Project.getPreparedComposeResourcesDir(sourceSet: KotlinSourceSet): Provider<File> = tasks
.named(
getPrepareComposeResourcesTaskName(sourceSet),
PrepareComposeResourcesTask::class.java
)
.flatMap { it.outputDir.asFile }
private fun getPrepareComposeResourcesTaskName(sourceSet: KotlinSourceSet) =
"prepareComposeResourcesTaskFor${sourceSet.name.uppercaseFirstChar()}"
internal abstract class CopyNonXmlValueResourcesTask : DefaultTask() {
@get:Inject
abstract val fileSystem: FileSystemOperations
@ -52,6 +68,7 @@ internal abstract class CopyNonXmlValueResourcesTask : DefaultTask() {
abstract val originalResourcesDir: DirectoryProperty
@get:InputFiles
@get:SkipWhenEmpty
val realInputFiles = originalResourcesDir.map { dir ->
dir.asFileTree.matching { it.exclude("values*/*.xml") }
}
@ -67,7 +84,7 @@ internal abstract class CopyNonXmlValueResourcesTask : DefaultTask() {
@TaskAction
fun run() {
realOutputFiles.get().forEach { f -> f.delete() }
fileSystem.copy { copy ->
fileSystem.copy { copy ->
copy.includeEmptyDirs = false
copy.from(originalResourcesDir) {
it.exclude("values*/*.xml")
@ -79,16 +96,18 @@ internal abstract class CopyNonXmlValueResourcesTask : DefaultTask() {
internal abstract class PrepareComposeResourcesTask : DefaultTask() {
@get:InputFiles
@get:SkipWhenEmpty
abstract val convertedXmls: Property<FileTree>
@get:InputFiles
@get:SkipWhenEmpty
abstract val copiedNonXmls: Property<FileTree>
@get:OutputDirectory
abstract val outputDir: Property<File>
abstract val outputDir: DirectoryProperty
@TaskAction
fun run() {}
fun run() = Unit
}
internal data class ValueResourceRecord(
@ -119,10 +138,14 @@ internal abstract class XmlValuesConverterTask : DefaultTask() {
private const val FORMAT_VERSION = 0
}
@get:Input
abstract val fileSuffix: Property<String>
@get:Internal
abstract val originalResourcesDir: DirectoryProperty
@get:InputFiles
@get:SkipWhenEmpty
val realInputFiles = originalResourcesDir.map { dir ->
dir.asFileTree.matching { it.include("values*/*.xml") }
}
@ -132,12 +155,14 @@ internal abstract class XmlValuesConverterTask : DefaultTask() {
@get:OutputFiles
val realOutputFiles = outputDir.map { dir ->
dir.asFileTree.matching { it.include("values*/*.$CONVERTED_RESOURCE_EXT") }
val suffix = fileSuffix.get()
dir.asFileTree.matching { it.include("values*/*.$suffix.$CONVERTED_RESOURCE_EXT") }
}
@TaskAction
fun run() {
val outDir = outputDir.get().asFile
val suffix = fileSuffix.get()
realOutputFiles.get().forEach { f -> f.delete() }
originalResourcesDir.get().asFile.listNotHiddenFiles().forEach { valuesDir ->
if (valuesDir.isDirectory && valuesDir.name.startsWith("values")) {
@ -145,7 +170,7 @@ internal abstract class XmlValuesConverterTask : DefaultTask() {
if (f.extension.equals("xml", true)) {
val output = outDir
.resolve(f.parentFile.name)
.resolve(f.nameWithoutExtension + ".$CONVERTED_RESOURCE_EXT")
.resolve(f.nameWithoutExtension + ".$suffix.$CONVERTED_RESOURCE_EXT")
output.parentFile.mkdirs()
convert(f, output)
}
@ -195,6 +220,7 @@ internal abstract class XmlValuesConverterTask : DefaultTask() {
quantity.uppercase() + ":" + content.asBase64()
}
}
else -> error("Unknown string resource type: '$type'")
}
return ValueResourceRecord(type, key, value)

65
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ResClassGeneration.kt

@ -1,65 +0,0 @@
package org.jetbrains.compose.resources
import org.gradle.api.Project
import org.gradle.api.provider.Provider
import org.jetbrains.compose.ComposePlugin
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import java.io.File
internal fun Project.configureGenerationComposeResClass(
commonComposeResourcesDir: Provider<File>,
commonSourceSet: KotlinSourceSet,
config: Provider<ResourcesExtension>,
generateModulePath: Boolean
) {
logger.info("Configure accessors for '${commonSourceSet.name}'")
//lazy check a dependency on the Resources library
val shouldGenerateResClass = config.map {
when (it.generateResClass) {
ResourcesExtension.ResourceClassGeneration.Auto -> {
configurations.run {
//because the implementation configuration doesn't extend the api in the KGP ¯\_(ツ)_/¯
getByName(commonSourceSet.implementationConfigurationName).allDependencies +
getByName(commonSourceSet.apiConfigurationName).allDependencies
}.any { dep ->
val depStringNotation = dep.let { "${it.group}:${it.name}:${it.version}" }
depStringNotation == ComposePlugin.CommonComponentsDependencies.resources
}
}
ResourcesExtension.ResourceClassGeneration.Always -> {
true
}
ResourcesExtension.ResourceClassGeneration.Never -> {
false
}
}
}
val genTask = tasks.register(
"generateComposeResClass",
GenerateResClassTask::class.java
) { task ->
task.packageName.set(config.getResourcePackage(project))
task.shouldGenerateResClass.set(shouldGenerateResClass)
task.makeResClassPublic.set(config.map { it.publicResClass })
task.resDir.set(commonComposeResourcesDir)
task.codeDir.set(layout.buildDirectory.dir("$RES_GEN_DIR/kotlin").map { it.asFile })
if (generateModulePath) {
task.moduleDir.set(config.getModuleResourcesDir(project))
}
}
//register generated source set
commonSourceSet.kotlin.srcDir(genTask.map { it.codeDir })
//setup task execution during IDE import
tasks.configureEach {
if (it.name == "prepareKotlinIdeaImport") {
it.dependsOn(genTask)
}
}
}

50
gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt

@ -14,9 +14,9 @@ class ResourcesTest : GradlePluginTestBase() {
@Test
fun testGeneratedAccessors(): Unit = with(testProject("misc/commonResources")) {
//check generated resource's accessors
gradle("generateComposeResClass").checks {
gradle("prepareKotlinIdeaImport").checks {
assertDirectoriesContentEquals(
file("build/generated/compose/resourceGenerator/kotlin/app/group/resources_test/generated/resources"),
file("build/generated/compose/resourceGenerator/kotlin"),
file("expected")
)
}
@ -25,17 +25,17 @@ class ResourcesTest : GradlePluginTestBase() {
file("src/commonMain/composeResources/drawable/vector_2.xml").renameTo(
file("src/commonMain/composeResources/drawable/vector_3.xml")
)
gradle("generateComposeResClass").checks {
gradle("prepareKotlinIdeaImport").checks {
assertNotEqualTextFiles(
file("build/generated/compose/resourceGenerator/kotlin/app/group/resources_test/generated/resources/Drawable0.kt"),
file("expected/Drawable0.kt")
file("build/generated/compose/resourceGenerator/kotlin/commonMainResourceAccessors/app/group/resources_test/generated/resources/Drawable0.commonMain.kt"),
file("expected/commonMainResourceAccessors/app/group/resources_test/generated/resources/Drawable0.commonMain.kt")
)
}
file("src/commonMain/composeResources/drawable-en").renameTo(
file("src/commonMain/composeResources/drawable-rent")
)
gradle("generateComposeResClass").checks {
gradle("prepareKotlinIdeaImport").checks {
check.logContains(
"""
contains unknown qualifier: 'rent'.
@ -46,7 +46,7 @@ class ResourcesTest : GradlePluginTestBase() {
file("src/commonMain/composeResources/drawable-rent").renameTo(
file("src/commonMain/composeResources/drawable-rUS-en")
)
gradle("generateComposeResClass").checks {
gradle("prepareKotlinIdeaImport").checks {
check.logContains(
"""
Region qualifier must be declared after language: 'en-rUS'.
@ -57,7 +57,7 @@ class ResourcesTest : GradlePluginTestBase() {
file("src/commonMain/composeResources/drawable-rUS-en").renameTo(
file("src/commonMain/composeResources/drawable-rUS")
)
gradle("generateComposeResClass").checks {
gradle("prepareKotlinIdeaImport").checks {
check.logContains(
"""
Region qualifier must be used only with language.
@ -68,7 +68,7 @@ class ResourcesTest : GradlePluginTestBase() {
file("src/commonMain/composeResources/drawable-rUS").renameTo(
file("src/commonMain/composeResources/drawable-en-fr")
)
gradle("generateComposeResClass").checks {
gradle("prepareKotlinIdeaImport").checks {
check.logContains(
"""
contains repetitive qualifiers: 'en' and 'fr'.
@ -79,7 +79,7 @@ class ResourcesTest : GradlePluginTestBase() {
file("src/commonMain/composeResources/drawable-en-fr").renameTo(
file("src/commonMain/composeResources/image")
)
gradle("generateComposeResClass").checks {
gradle("prepareKotlinIdeaImport").checks {
check.logContains(
"""
Unknown resource type: 'image'
@ -90,7 +90,7 @@ class ResourcesTest : GradlePluginTestBase() {
file("src/commonMain/composeResources/image").renameTo(
file("src/commonMain/composeResources/files-de")
)
gradle("generateComposeResClass").checks {
gradle("prepareKotlinIdeaImport").checks {
check.logContains(
"""
The 'files' directory doesn't support qualifiers: 'files-de'.
@ -101,7 +101,7 @@ class ResourcesTest : GradlePluginTestBase() {
file("src/commonMain/composeResources/files-de").renameTo(
file("src/commonMain/composeResources/strings")
)
gradle("generateComposeResClass").checks {
gradle("prepareKotlinIdeaImport").checks {
check.logContains(
"""
Unknown resource type: 'strings'.
@ -112,7 +112,7 @@ class ResourcesTest : GradlePluginTestBase() {
file("src/commonMain/composeResources/strings").renameTo(
file("src/commonMain/composeResources/string-us")
)
gradle("generateComposeResClass").checks {
gradle("prepareKotlinIdeaImport").checks {
check.logContains(
"""
Forbidden directory name 'string-us'! String resources should be declared in 'values/strings.xml'.
@ -137,9 +137,9 @@ class ResourcesTest : GradlePluginTestBase() {
""".trimIndent()
}
gradle("generateComposeResClass").checks {
gradle("prepareKotlinIdeaImport").checks {
assertDirectoriesContentEquals(
file("build/generated/compose/resourceGenerator/kotlin/my/lib/res"),
file("build/generated/compose/resourceGenerator/kotlin"),
file("expected-open-res")
)
}
@ -382,7 +382,8 @@ class ResourcesTest : GradlePluginTestBase() {
file.parentFile.name.startsWith("value") &&
file.extension.equals("xml", true)
) {
file.parentFile.resolve(file.nameWithoutExtension + "." + XmlValuesConverterTask.CONVERTED_RESOURCE_EXT)
val cvrSuffix = file.parentFile.parentFile.parentFile.name
file.parentFile.resolve("${file.nameWithoutExtension}.$cvrSuffix.${XmlValuesConverterTask.CONVERTED_RESOURCE_EXT}")
} else {
file
}
@ -431,7 +432,7 @@ class ResourcesTest : GradlePluginTestBase() {
fun testUpToDateChecks(): Unit = with(testProject("misc/commonResources")) {
gradle("prepareKotlinIdeaImport").checks {
check.taskSuccessful(":generateComposeResClass")
assertTrue(file("build/generated/compose/resourceGenerator/kotlin/app/group/resources_test/generated/resources/Res.kt").exists())
assertTrue(file("build/generated/compose/resourceGenerator/kotlin/commonResClass/app/group/resources_test/generated/resources/Res.kt").exists())
}
gradle("prepareKotlinIdeaImport").checks {
check.taskUpToDate(":generateComposeResClass")
@ -445,7 +446,7 @@ class ResourcesTest : GradlePluginTestBase() {
}
gradle("prepareKotlinIdeaImport").checks {
check.taskSuccessful(":generateComposeResClass")
assertFalse(file("build/generated/compose/resourceGenerator/kotlin/app/group/resources_test/generated/resources/Res.kt").exists())
assertFalse(file("build/generated/compose/resourceGenerator/kotlin/commonResClass/app/group/resources_test/generated/resources/Res.kt").exists())
}
modifyText("build.gradle.kts") { str ->
@ -462,8 +463,8 @@ class ResourcesTest : GradlePluginTestBase() {
}
gradle("prepareKotlinIdeaImport").checks {
check.taskSuccessful(":generateComposeResClass")
assertFalse(file("build/generated/compose/resourceGenerator/kotlin/app/group/resources_test/generated/resources/Res.kt").exists())
assertTrue(file("build/generated/compose/resourceGenerator/kotlin/io/company/resources_test/generated/resources/Res.kt").exists())
assertFalse(file("build/generated/compose/resourceGenerator/kotlin/commonResClass/app/group/resources_test/generated/resources/Res.kt").exists())
assertTrue(file("build/generated/compose/resourceGenerator/kotlin/commonResClass/io/company/resources_test/generated/resources/Res.kt").exists())
}
}
@ -471,7 +472,7 @@ class ResourcesTest : GradlePluginTestBase() {
fun testEmptyResClass(): Unit = with(testProject("misc/emptyResources")) {
gradle("generateComposeResClass").checks {
assertDirectoriesContentEquals(
file("build/generated/compose/resourceGenerator/kotlin/app/group/empty_res/generated/resources"),
file("build/generated/compose/resourceGenerator/kotlin/commonResClass/app/group/empty_res/generated/resources"),
file("expected")
)
}
@ -479,13 +480,12 @@ class ResourcesTest : GradlePluginTestBase() {
@Test
fun testJvmOnlyProject(): Unit = with(testProject("misc/jvmOnlyResources")) {
gradle("generateComposeResClass").checks {
gradle("jar").checks {
assertDirectoriesContentEquals(
file("build/generated/compose/resourceGenerator/kotlin/me/app/jvmonlyresources/generated/resources"),
file("build/generated/compose/resourceGenerator/kotlin"),
file("expected")
)
}
gradle("jar")
}
//https://github.com/gmazzo/gradle-buildconfig-plugin/issues/131
@ -509,8 +509,10 @@ class ResourcesTest : GradlePluginTestBase() {
}
val expectedFilesCount = expected.walkTopDown()
.filter { !it.isDirectory }
.map { it.toPath().relativeTo(expectedPath) }.sorted().joinToString("\n")
val actualFilesCount = actual.walkTopDown()
.filter { !it.isDirectory }
.map { it.toPath().relativeTo(actualPath) }.sorted().joinToString("\n")
assertEquals(expectedFilesCount, actualFilesCount)
}

26
gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected-open-res/androidMainResourceAccessors/my/lib/res/String0.androidMain.kt

@ -0,0 +1,26 @@
@file:OptIn(org.jetbrains.compose.resources.InternalResourceApi::class)
package my.lib.res
import kotlin.OptIn
import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.StringResource
@ExperimentalResourceApi
private object AndroidMainString0 {
public val android_str: StringResource by
lazy { init_android_str() }
}
@ExperimentalResourceApi
public val Res.string.android_str: StringResource
get() = AndroidMainString0.android_str
@ExperimentalResourceApi
private fun init_android_str(): StringResource = org.jetbrains.compose.resources.StringResource(
"string:android_str", "android_str",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.androidMain.cvr", 10,
39),
)
)

10
gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected-open-res/Drawable0.kt → gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected-open-res/commonMainResourceAccessors/my/lib/res/Drawable0.commonMain.kt

@ -7,7 +7,7 @@ import org.jetbrains.compose.resources.DrawableResource
import org.jetbrains.compose.resources.ExperimentalResourceApi
@ExperimentalResourceApi
private object Drawable0 {
private object CommonMainDrawable0 {
public val _3_strange_name: DrawableResource by
lazy { init__3_strange_name() }
@ -23,7 +23,7 @@ private object Drawable0 {
@ExperimentalResourceApi
public val Res.drawable._3_strange_name: DrawableResource
get() = Drawable0._3_strange_name
get() = CommonMainDrawable0._3_strange_name
@ExperimentalResourceApi
private fun init__3_strange_name(): DrawableResource =
@ -36,7 +36,7 @@ private fun init__3_strange_name(): DrawableResource =
@ExperimentalResourceApi
public val Res.drawable.camelCaseName: DrawableResource
get() = Drawable0.camelCaseName
get() = CommonMainDrawable0.camelCaseName
@ExperimentalResourceApi
private fun init_camelCaseName(): DrawableResource =
@ -49,7 +49,7 @@ private fun init_camelCaseName(): DrawableResource =
@ExperimentalResourceApi
public val Res.drawable.vector: DrawableResource
get() = Drawable0.vector
get() = CommonMainDrawable0.vector
@ExperimentalResourceApi
private fun init_vector(): DrawableResource = org.jetbrains.compose.resources.DrawableResource(
@ -74,7 +74,7 @@ private fun init_vector(): DrawableResource = org.jetbrains.compose.resources.Dr
@ExperimentalResourceApi
public val Res.drawable.vector_2: DrawableResource
get() = Drawable0.vector_2
get() = CommonMainDrawable0.vector_2
@ExperimentalResourceApi
private fun init_vector_2(): DrawableResource = org.jetbrains.compose.resources.DrawableResource(

4
gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected-open-res/Font0.kt → gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected-open-res/commonMainResourceAccessors/my/lib/res/Font0.commonMain.kt

@ -7,14 +7,14 @@ import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.FontResource
@ExperimentalResourceApi
private object Font0 {
private object CommonMainFont0 {
public val emptyFont: FontResource by
lazy { init_emptyFont() }
}
@ExperimentalResourceApi
public val Res.font.emptyFont: FontResource
get() = Font0.emptyFont
get() = CommonMainFont0.emptyFont
@ExperimentalResourceApi
private fun init_emptyFont(): FontResource = org.jetbrains.compose.resources.FontResource(

7
gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected-open-res/Plurals0.kt → gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected-open-res/commonMainResourceAccessors/my/lib/res/Plurals0.commonMain.kt

@ -7,20 +7,21 @@ import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.PluralStringResource
@ExperimentalResourceApi
private object Plurals0 {
private object CommonMainPlurals0 {
public val numberOfSongsAvailable: PluralStringResource by
lazy { init_numberOfSongsAvailable() }
}
@ExperimentalResourceApi
public val Res.plurals.numberOfSongsAvailable: PluralStringResource
get() = Plurals0.numberOfSongsAvailable
get() = CommonMainPlurals0.numberOfSongsAvailable
@ExperimentalResourceApi
private fun init_numberOfSongsAvailable(): PluralStringResource =
org.jetbrains.compose.resources.PluralStringResource(
"plurals:numberOfSongsAvailable", "numberOfSongsAvailable",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.cvr", 10, 124),
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.commonMain.cvr", 10,
124),
)
)

37
gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected-open-res/String0.kt → gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected-open-res/commonMainResourceAccessors/my/lib/res/String0.commonMain.kt

@ -7,7 +7,7 @@ import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.StringResource
@ExperimentalResourceApi
private object String0 {
private object CommonMainString0 {
public val PascalCase: StringResource by
lazy { init_PascalCase() }
@ -32,84 +32,91 @@ private object String0 {
@ExperimentalResourceApi
public val Res.string.PascalCase: StringResource
get() = String0.PascalCase
get() = CommonMainString0.PascalCase
@ExperimentalResourceApi
private fun init_PascalCase(): StringResource = org.jetbrains.compose.resources.StringResource(
"string:PascalCase", "PascalCase",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.cvr", 172, 34),
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.commonMain.cvr", 172,
34),
)
)
@ExperimentalResourceApi
public val Res.string._1_kebab_case: StringResource
get() = String0._1_kebab_case
get() = CommonMainString0._1_kebab_case
@ExperimentalResourceApi
private fun init__1_kebab_case(): StringResource = org.jetbrains.compose.resources.StringResource(
"string:_1_kebab_case", "_1_kebab_case",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.cvr", 135, 36),
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.commonMain.cvr", 135,
36),
)
)
@ExperimentalResourceApi
public val Res.string.app_name: StringResource
get() = String0.app_name
get() = CommonMainString0.app_name
@ExperimentalResourceApi
private fun init_app_name(): StringResource = org.jetbrains.compose.resources.StringResource(
"string:app_name", "app_name",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.cvr", 207, 44),
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.commonMain.cvr", 207,
44),
)
)
@ExperimentalResourceApi
public val Res.string.camelCase: StringResource
get() = String0.camelCase
get() = CommonMainString0.camelCase
@ExperimentalResourceApi
private fun init_camelCase(): StringResource = org.jetbrains.compose.resources.StringResource(
"string:camelCase", "camelCase",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.cvr", 252, 29),
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.commonMain.cvr", 252,
29),
)
)
@ExperimentalResourceApi
public val Res.string.hello: StringResource
get() = String0.hello
get() = CommonMainString0.hello
@ExperimentalResourceApi
private fun init_hello(): StringResource = org.jetbrains.compose.resources.StringResource(
"string:hello", "hello",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.cvr", 282, 37),
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.commonMain.cvr", 282,
37),
)
)
@ExperimentalResourceApi
public val Res.string.multi_line: StringResource
get() = String0.multi_line
get() = CommonMainString0.multi_line
@ExperimentalResourceApi
private fun init_multi_line(): StringResource = org.jetbrains.compose.resources.StringResource(
"string:multi_line", "multi_line",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.cvr", 320, 178),
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.commonMain.cvr", 320,
178),
)
)
@ExperimentalResourceApi
public val Res.string.str_template: StringResource
get() = String0.str_template
get() = CommonMainString0.str_template
@ExperimentalResourceApi
private fun init_str_template(): StringResource = org.jetbrains.compose.resources.StringResource(
"string:str_template", "str_template",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.cvr", 499, 76),
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.commonMain.cvr", 499,
76),
)
)

0
gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected-open-res/Res.kt → gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected-open-res/commonResClass/my/lib/res/Res.kt

26
gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected-open-res/desktopMainResourceAccessors/my/lib/res/String0.desktopMain.kt

@ -0,0 +1,26 @@
@file:OptIn(org.jetbrains.compose.resources.InternalResourceApi::class)
package my.lib.res
import kotlin.OptIn
import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.StringResource
@ExperimentalResourceApi
private object DesktopMainString0 {
public val desktop_str: StringResource by
lazy { init_desktop_str() }
}
@ExperimentalResourceApi
public val Res.string.desktop_str: StringResource
get() = DesktopMainString0.desktop_str
@ExperimentalResourceApi
private fun init_desktop_str(): StringResource = org.jetbrains.compose.resources.StringResource(
"string:desktop_str", "desktop_str",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(),
"values/desktop_strings.desktopMain.cvr", 10, 39),
)
)

26
gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected/androidMainResourceAccessors/app/group/resources_test/generated/resources/String0.androidMain.kt

@ -0,0 +1,26 @@
@file:OptIn(org.jetbrains.compose.resources.InternalResourceApi::class)
package app.group.resources_test.generated.resources
import kotlin.OptIn
import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.StringResource
@ExperimentalResourceApi
private object AndroidMainString0 {
public val android_str: StringResource by
lazy { init_android_str() }
}
@ExperimentalResourceApi
internal val Res.string.android_str: StringResource
get() = AndroidMainString0.android_str
@ExperimentalResourceApi
private fun init_android_str(): StringResource = org.jetbrains.compose.resources.StringResource(
"string:android_str", "android_str",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.androidMain.cvr", 10,
39),
)
)

10
gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected/Drawable0.kt → gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected/commonMainResourceAccessors/app/group/resources_test/generated/resources/Drawable0.commonMain.kt

@ -7,7 +7,7 @@ import org.jetbrains.compose.resources.DrawableResource
import org.jetbrains.compose.resources.ExperimentalResourceApi
@ExperimentalResourceApi
private object Drawable0 {
private object CommonMainDrawable0 {
public val _3_strange_name: DrawableResource by
lazy { init__3_strange_name() }
@ -23,7 +23,7 @@ private object Drawable0 {
@ExperimentalResourceApi
internal val Res.drawable._3_strange_name: DrawableResource
get() = Drawable0._3_strange_name
get() = CommonMainDrawable0._3_strange_name
@ExperimentalResourceApi
private fun init__3_strange_name(): DrawableResource =
@ -36,7 +36,7 @@ private fun init__3_strange_name(): DrawableResource =
@ExperimentalResourceApi
internal val Res.drawable.camelCaseName: DrawableResource
get() = Drawable0.camelCaseName
get() = CommonMainDrawable0.camelCaseName
@ExperimentalResourceApi
private fun init_camelCaseName(): DrawableResource =
@ -49,7 +49,7 @@ private fun init_camelCaseName(): DrawableResource =
@ExperimentalResourceApi
internal val Res.drawable.vector: DrawableResource
get() = Drawable0.vector
get() = CommonMainDrawable0.vector
@ExperimentalResourceApi
private fun init_vector(): DrawableResource = org.jetbrains.compose.resources.DrawableResource(
@ -74,7 +74,7 @@ private fun init_vector(): DrawableResource = org.jetbrains.compose.resources.Dr
@ExperimentalResourceApi
internal val Res.drawable.vector_2: DrawableResource
get() = Drawable0.vector_2
get() = CommonMainDrawable0.vector_2
@ExperimentalResourceApi
private fun init_vector_2(): DrawableResource = org.jetbrains.compose.resources.DrawableResource(

4
gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected/Font0.kt → gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected/commonMainResourceAccessors/app/group/resources_test/generated/resources/Font0.commonMain.kt

@ -7,14 +7,14 @@ import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.FontResource
@ExperimentalResourceApi
private object Font0 {
private object CommonMainFont0 {
public val emptyFont: FontResource by
lazy { init_emptyFont() }
}
@ExperimentalResourceApi
internal val Res.font.emptyFont: FontResource
get() = Font0.emptyFont
get() = CommonMainFont0.emptyFont
@ExperimentalResourceApi
private fun init_emptyFont(): FontResource = org.jetbrains.compose.resources.FontResource(

7
gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected/Plurals0.kt → gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected/commonMainResourceAccessors/app/group/resources_test/generated/resources/Plurals0.commonMain.kt

@ -7,20 +7,21 @@ import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.PluralStringResource
@ExperimentalResourceApi
private object Plurals0 {
private object CommonMainPlurals0 {
public val numberOfSongsAvailable: PluralStringResource by
lazy { init_numberOfSongsAvailable() }
}
@ExperimentalResourceApi
internal val Res.plurals.numberOfSongsAvailable: PluralStringResource
get() = Plurals0.numberOfSongsAvailable
get() = CommonMainPlurals0.numberOfSongsAvailable
@ExperimentalResourceApi
private fun init_numberOfSongsAvailable(): PluralStringResource =
org.jetbrains.compose.resources.PluralStringResource(
"plurals:numberOfSongsAvailable", "numberOfSongsAvailable",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.cvr", 10, 124),
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.commonMain.cvr", 10,
124),
)
)

37
gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected/String0.kt → gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected/commonMainResourceAccessors/app/group/resources_test/generated/resources/String0.commonMain.kt

@ -7,7 +7,7 @@ import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.StringResource
@ExperimentalResourceApi
private object String0 {
private object CommonMainString0 {
public val PascalCase: StringResource by
lazy { init_PascalCase() }
@ -32,84 +32,91 @@ private object String0 {
@ExperimentalResourceApi
internal val Res.string.PascalCase: StringResource
get() = String0.PascalCase
get() = CommonMainString0.PascalCase
@ExperimentalResourceApi
private fun init_PascalCase(): StringResource = org.jetbrains.compose.resources.StringResource(
"string:PascalCase", "PascalCase",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.cvr", 172, 34),
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.commonMain.cvr", 172,
34),
)
)
@ExperimentalResourceApi
internal val Res.string._1_kebab_case: StringResource
get() = String0._1_kebab_case
get() = CommonMainString0._1_kebab_case
@ExperimentalResourceApi
private fun init__1_kebab_case(): StringResource = org.jetbrains.compose.resources.StringResource(
"string:_1_kebab_case", "_1_kebab_case",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.cvr", 135, 36),
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.commonMain.cvr", 135,
36),
)
)
@ExperimentalResourceApi
internal val Res.string.app_name: StringResource
get() = String0.app_name
get() = CommonMainString0.app_name
@ExperimentalResourceApi
private fun init_app_name(): StringResource = org.jetbrains.compose.resources.StringResource(
"string:app_name", "app_name",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.cvr", 207, 44),
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.commonMain.cvr", 207,
44),
)
)
@ExperimentalResourceApi
internal val Res.string.camelCase: StringResource
get() = String0.camelCase
get() = CommonMainString0.camelCase
@ExperimentalResourceApi
private fun init_camelCase(): StringResource = org.jetbrains.compose.resources.StringResource(
"string:camelCase", "camelCase",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.cvr", 252, 29),
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.commonMain.cvr", 252,
29),
)
)
@ExperimentalResourceApi
internal val Res.string.hello: StringResource
get() = String0.hello
get() = CommonMainString0.hello
@ExperimentalResourceApi
private fun init_hello(): StringResource = org.jetbrains.compose.resources.StringResource(
"string:hello", "hello",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.cvr", 282, 37),
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.commonMain.cvr", 282,
37),
)
)
@ExperimentalResourceApi
internal val Res.string.multi_line: StringResource
get() = String0.multi_line
get() = CommonMainString0.multi_line
@ExperimentalResourceApi
private fun init_multi_line(): StringResource = org.jetbrains.compose.resources.StringResource(
"string:multi_line", "multi_line",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.cvr", 320, 178),
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.commonMain.cvr", 320,
178),
)
)
@ExperimentalResourceApi
internal val Res.string.str_template: StringResource
get() = String0.str_template
get() = CommonMainString0.str_template
@ExperimentalResourceApi
private fun init_str_template(): StringResource = org.jetbrains.compose.resources.StringResource(
"string:str_template", "str_template",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.cvr", 499, 76),
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.commonMain.cvr", 499,
76),
)
)

0
gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected/Res.kt → gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected/commonResClass/app/group/resources_test/generated/resources/Res.kt

26
gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected/desktopMainResourceAccessors/app/group/resources_test/generated/resources/String0.desktopMain.kt

@ -0,0 +1,26 @@
@file:OptIn(org.jetbrains.compose.resources.InternalResourceApi::class)
package app.group.resources_test.generated.resources
import kotlin.OptIn
import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.StringResource
@ExperimentalResourceApi
private object DesktopMainString0 {
public val desktop_str: StringResource by
lazy { init_desktop_str() }
}
@ExperimentalResourceApi
internal val Res.string.desktop_str: StringResource
get() = DesktopMainString0.desktop_str
@ExperimentalResourceApi
private fun init_desktop_str(): StringResource = org.jetbrains.compose.resources.StringResource(
"string:desktop_str", "desktop_str",
setOf(
org.jetbrains.compose.resources.ResourceItem(setOf(),
"values/desktop_strings.desktopMain.cvr", 10, 39),
)
)

3
gradle-plugins/compose/src/test/test-projects/misc/commonResources/src/androidMain/composeResources/values/strings.xml

@ -0,0 +1,3 @@
<resources>
<string name="android_str">Android string</string>
</resources>

3
gradle-plugins/compose/src/test/test-projects/misc/commonResources/src/desktopMain/composeResources/values/desktop_strings.xml

@ -0,0 +1,3 @@
<resources>
<string name="desktop_str">Desktop string</string>
</resources>

0
gradle-plugins/compose/src/test/test-projects/misc/jvmOnlyResources/expected/Res.kt → gradle-plugins/compose/src/test/test-projects/misc/jvmOnlyResources/expected/commonResClass/me/app/jvmonlyresources/generated/resources/Res.kt

4
gradle-plugins/compose/src/test/test-projects/misc/jvmOnlyResources/expected/Drawable0.kt → gradle-plugins/compose/src/test/test-projects/misc/jvmOnlyResources/expected/mainResourceAccessors/me/app/jvmonlyresources/generated/resources/Drawable0.main.kt

@ -7,14 +7,14 @@ import org.jetbrains.compose.resources.DrawableResource
import org.jetbrains.compose.resources.ExperimentalResourceApi
@ExperimentalResourceApi
private object Drawable0 {
private object MainDrawable0 {
public val vector: DrawableResource by
lazy { init_vector() }
}
@ExperimentalResourceApi
internal val Res.drawable.vector: DrawableResource
get() = Drawable0.vector
get() = MainDrawable0.vector
@ExperimentalResourceApi
private fun init_vector(): DrawableResource = org.jetbrains.compose.resources.DrawableResource(

3
gradle-plugins/compose/src/test/test-projects/misc/kmpResourcePublication/appModule/src/androidMain/composeResources/values/android_strings.xml

@ -0,0 +1,3 @@
<resources>
<string name="android_str">Android string</string>
</resources>

10
gradle-plugins/compose/src/test/test-projects/misc/kmpResourcePublication/appModule/src/androidMain/kotlin/me/sample/app/App.android.kt

@ -0,0 +1,10 @@
package me.sample.app
import androidx.compose.runtime.Composable
import kmpresourcepublication.appmodule.generated.resources.Res
import kmpresourcepublication.appmodule.generated.resources.android_str
import org.jetbrains.compose.resources.stringResource
@Composable
actual fun getPlatformSpecificString(): String =
stringResource(Res.string.android_str)

5
gradle-plugins/compose/src/test/test-projects/misc/kmpResourcePublication/appModule/src/commonMain/kotlin/me/sample/app/App.kt

@ -18,4 +18,7 @@ fun App() {
MyLibraryText(txt = txt)
MyLibraryIcon(Modifier)
}
}
}
@Composable
expect fun getPlatformSpecificString(): String

3
gradle-plugins/compose/src/test/test-projects/misc/kmpResourcePublication/appModule/src/iosMain/composeResources/values/ios_strings.xml

@ -0,0 +1,3 @@
<resources>
<string name="iOS_str">Ios string</string>
</resources>

10
gradle-plugins/compose/src/test/test-projects/misc/kmpResourcePublication/appModule/src/iosMain/kotlin/me/sample/app/App.ios.kt

@ -0,0 +1,10 @@
package me.sample.app
import androidx.compose.runtime.Composable
import kmpresourcepublication.appmodule.generated.resources.Res
import kmpresourcepublication.appmodule.generated.resources.iOS_str
import org.jetbrains.compose.resources.stringResource
@Composable
actual fun getPlatformSpecificString(): String =
stringResource(Res.string.iOS_str)

3
gradle-plugins/compose/src/test/test-projects/misc/kmpResourcePublication/appModule/src/jsMain/composeResources/values/js_strings.xml

@ -0,0 +1,3 @@
<resources>
<string name="js_str">JS string</string>
</resources>

10
gradle-plugins/compose/src/test/test-projects/misc/kmpResourcePublication/appModule/src/jsMain/kotlin/me/sample/app/App.js.kt

@ -0,0 +1,10 @@
package me.sample.app
import androidx.compose.runtime.Composable
import kmpresourcepublication.appmodule.generated.resources.Res
import kmpresourcepublication.appmodule.generated.resources.js_str
import org.jetbrains.compose.resources.stringResource
@Composable
actual fun getPlatformSpecificString(): String =
stringResource(Res.string.js_str)

3
gradle-plugins/compose/src/test/test-projects/misc/kmpResourcePublication/appModule/src/jvmMain/composeResources/values/jvm_strings.xml

@ -0,0 +1,3 @@
<resources>
<string name="desktop_str">Desktop string</string>
</resources>

10
gradle-plugins/compose/src/test/test-projects/misc/kmpResourcePublication/appModule/src/jvmMain/kotlin/me/sample/app/App.jvm.kt

@ -0,0 +1,10 @@
package me.sample.app
import androidx.compose.runtime.Composable
import kmpresourcepublication.appmodule.generated.resources.Res
import kmpresourcepublication.appmodule.generated.resources.desktop_str
import org.jetbrains.compose.resources.stringResource
@Composable
actual fun getPlatformSpecificString(): String =
stringResource(Res.string.desktop_str)

3
gradle-plugins/compose/src/test/test-projects/misc/kmpResourcePublication/appModule/src/wasmJsMain/composeResources/values/wasm_strings.xml

@ -0,0 +1,3 @@
<resources>
<string name="wasm_str">Wasm string</string>
</resources>

10
gradle-plugins/compose/src/test/test-projects/misc/kmpResourcePublication/appModule/src/wasmJsMain/kotlin/me/sample/app/App.wasmJs.kt

@ -0,0 +1,10 @@
package me.sample.app
import androidx.compose.runtime.Composable
import kmpresourcepublication.appmodule.generated.resources.Res
import kmpresourcepublication.appmodule.generated.resources.wasm_str
import org.jetbrains.compose.resources.stringResource
@Composable
actual fun getPlatformSpecificString(): String =
stringResource(Res.string.wasm_str)
Loading…
Cancel
Save