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 1 month 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.DefaultTask
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.FileCollection
import org.gradle.api.file.FileSystemOperations import org.gradle.api.file.FileSystemOperations
import org.gradle.api.provider.Property import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.* import org.gradle.api.tasks.*
import org.jetbrains.compose.internal.utils.registerTask import org.jetbrains.compose.internal.utils.registerTask
import org.jetbrains.compose.internal.utils.uppercaseFirstChar 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.mpp.KotlinJvmAndroidCompilation
import org.jetbrains.kotlin.gradle.plugin.sources.android.androidSourceSetInfoOrNull import org.jetbrains.kotlin.gradle.plugin.sources.android.androidSourceSetInfoOrNull
import org.jetbrains.kotlin.gradle.utils.ObservableSet import org.jetbrains.kotlin.gradle.utils.ObservableSet
import java.io.File
import javax.inject.Inject import javax.inject.Inject
@OptIn(ExperimentalKotlinGradlePluginApi::class) @OptIn(ExperimentalKotlinGradlePluginApi::class)
internal fun Project.configureAndroidComposeResources( internal fun Project.configureAndroidComposeResources(
kotlinExtension: KotlinMultiplatformExtension, kotlinExtension: KotlinMultiplatformExtension,
androidExtension: BaseExtension, androidExtension: BaseExtension
preparedCommonResources: Provider<File>
) { ) {
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] // 1) get the Kotlin Android Target Compilation -> [A]
// 2) get default source set name for the 'A' // 2) get default source set name for the 'A'
// 3) find the associated Android SourceSet in the AndroidExtension -> [B] // 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' // 4) get all source sets in the 'A' and add its resources to the 'B'
kotlinExtension.targets.withType(KotlinAndroidTarget::class.java).all { androidTarget -> kotlinExtension.targets.withType(KotlinAndroidTarget::class.java).all { androidTarget ->
androidTarget.compilations.all { compilation: KotlinJvmAndroidCompilation -> 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 -> compilation.defaultSourceSet.androidSourceSetInfoOrNull?.let { kotlinAndroidSourceSet ->
androidExtension.sourceSets androidExtension.sourceSets
.matching { it.name == kotlinAndroidSourceSet.androidSourceSetName } .matching { it.name == kotlinAndroidSourceSet.androidSourceSetName }
.all { androidSourceSet -> .all { androidSourceSet ->
(compilation.allKotlinSourceSets as? ObservableSet<KotlinSourceSet>)?.forAll { kotlinSourceSet -> (compilation.allKotlinSourceSets as? ObservableSet<KotlinSourceSet>)?.forAll { kotlinSourceSet ->
if (kotlinSourceSet.name == commonMain) { val preparedComposeResources = getPreparedComposeResourcesDir(kotlinSourceSet)
androidSourceSet.resources.srcDir(preparedCommonResources) androidSourceSet.resources.srcDirs(preparedComposeResources)
} else {
androidSourceSet.resources.srcDir( //fix for AGP < 8.0
projectDir.resolve("src/${kotlinSourceSet.name}/$COMPOSE_RESOURCES_DIR") //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 //copy fonts from the compose resources dir to android assets
val androidComponents = project.extensions.findByType(AndroidComponentsExtension::class.java) ?: return val androidComponents = project.extensions.findByType(AndroidComponentsExtension::class.java) ?: return
androidComponents.onVariants { variant -> 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>( val copyFonts = registerTask<CopyAndroidFontsToAssetsTask>(
"copy${variant.name.uppercaseFirstChar()}FontsToAndroidAssets" "copy${variant.name.uppercaseFirstChar()}FontsToAndroidAssets"
) { ) {
from.set(commonResourcesDir) from.set(variantResources)
} }
variant.sources?.assets?.addGeneratedSourceDirectory( variant.sources?.assets?.addGeneratedSourceDirectory(
taskProvider = copyFonts, taskProvider = copyFonts,
@ -83,7 +88,7 @@ internal abstract class CopyAndroidFontsToAssetsTask : DefaultTask() {
@get:InputFiles @get:InputFiles
@get:IgnoreEmptyDirectories @get:IgnoreEmptyDirectories
abstract val from: Property<File> abstract val from: Property<FileCollection>
@get:OutputDirectory @get:OutputDirectory
abstract val outputDirectory: DirectoryProperty 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 package org.jetbrains.compose.resources
import com.android.build.gradle.BaseExtension 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.Project
import org.gradle.api.provider.Provider import org.gradle.api.provider.Provider
import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.SourceSet
import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.TaskProvider
import org.gradle.util.GradleVersion 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_JVM_PLUGIN_ID
import org.jetbrains.compose.internal.KOTLIN_MPP_PLUGIN_ID import org.jetbrains.compose.internal.KOTLIN_MPP_PLUGIN_ID
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
@ -33,22 +36,18 @@ internal fun Project.configureComposeResources(extension: ResourcesExtension) {
private fun Project.onKgpApplied(config: Provider<ResourcesExtension>) { private fun Project.onKgpApplied(config: Provider<ResourcesExtension>) {
val kotlinExtension = project.extensions.getByType(KotlinMultiplatformExtension::class.java) 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 hasKmpResources = extraProperties.has(KMP_RES_EXT)
val currentGradleVersion = GradleVersion.current() val currentGradleVersion = GradleVersion.current()
val minGradleVersion = GradleVersion.version(MIN_GRADLE_VERSION_FOR_KMP_RESOURCES) val minGradleVersion = GradleVersion.version(MIN_GRADLE_VERSION_FOR_KMP_RESOURCES)
val kmpResourcesAreAvailable = hasKmpResources && currentGradleVersion >= minGradleVersion val kmpResourcesAreAvailable = hasKmpResources && currentGradleVersion >= minGradleVersion
if (kmpResourcesAreAvailable) { if (kmpResourcesAreAvailable) {
configureKmpResources(kotlinExtension, extraProperties.get(KMP_RES_EXT)!!, preparedCommonResources, config) configureKmpResources(kotlinExtension, extraProperties.get(KMP_RES_EXT)!!, config)
} else { } else {
if (!hasKmpResources) logger.info( if (!hasKmpResources) logger.info(
""" """
Compose resources publication requires Kotlin Gradle Plugin >= 2.0 Compose resources publication requires Kotlin Gradle Plugin >= 2.0
Current Kotlin Gradle Plugin is ${KotlinVersion.CURRENT} Current Kotlin Gradle Plugin is ${kotlinExtension.coreLibrariesVersion}
""".trimIndent() """.trimIndent()
) )
if (currentGradleVersion < minGradleVersion) logger.info( if (currentGradleVersion < minGradleVersion) logger.info(
@ -58,13 +57,31 @@ private fun Project.onKgpApplied(config: Provider<ResourcesExtension>) {
""".trimIndent() """.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 //when applied AGP then configure android resources
androidPluginIds.forEach { pluginId -> androidPluginIds.forEach { pluginId ->
plugins.withId(pluginId) { plugins.withId(pluginId) {
val androidExtension = project.extensions.getByType(BaseExtension::class.java) 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>) { private fun Project.onKotlinJvmApplied(config: Provider<ResourcesExtension>) {
val kotlinExtension = project.extensions.getByType(KotlinProjectExtension::class.java) val kotlinExtension = project.extensions.getByType(KotlinProjectExtension::class.java)
val main = SourceSet.MAIN_SOURCE_SET_NAME val main = SourceSet.MAIN_SOURCE_SET_NAME
val preparedCommonResources = prepareCommonResources(main) configureComposeResources(kotlinExtension, main, config)
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 }
} }
// sourceSet.resources.srcDirs doesn't work for Android targets. // sourceSet.resources.srcDirs doesn't work for Android targets.
// Android resources should be configured separately // Android resources should be configured separately
private fun Project.configureComposeResources( private fun Project.configureComposeResources(
kotlinExtension: KotlinProjectExtension, kotlinExtension: KotlinProjectExtension,
commonSourceSetName: String, resClassSourceSetName: String,
preparedCommonResources: Provider<File>,
config: Provider<ResourcesExtension> config: Provider<ResourcesExtension>
) { ) {
logger.info("Configure compose resources") logger.info("Configure compose resources")
configureComposeResourcesGeneration(kotlinExtension, resClassSourceSetName, config, false)
kotlinExtension.sourceSets.all { sourceSet -> kotlinExtension.sourceSets.all { sourceSet ->
val sourceSetName = sourceSet.name sourceSet.resources.srcDirs(getPreparedComposeResourcesDir(sourceSet))
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)
}
} }
} }

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 package org.jetbrains.compose.resources
import org.gradle.api.DefaultTask 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.Property
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.* import org.gradle.api.tasks.*
import java.io.File 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. * 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 @get:Input
abstract val packageName: Property<String> abstract val packageName: Property<String>
@get:Input @get:Input
@get:Optional @get:Optional
abstract val moduleDir: Property<File> abstract val packagingDir: Property<File>
@get:Input @get:Input
abstract val shouldGenerateResClass: Property<Boolean> abstract val shouldGenerateCode: Property<Boolean>
@get:Input @get:Input
abstract val makeResClassPublic: Property<Boolean> abstract val makeAccessorsPublic: Property<Boolean>
@get:InputFiles
@get:PathSensitive(PathSensitivity.RELATIVE)
abstract val resDir: Property<File>
@get:OutputDirectory @get:OutputDirectory
abstract val codeDir: Property<File> abstract val codeDir: DirectoryProperty
@TaskAction @TaskAction
fun generate() { fun generate() {
try { try {
val kotlinDir = codeDir.get() val dir = codeDir.get().asFile
logger.info("Clean directory $kotlinDir") dir.deleteRecursively()
kotlinDir.deleteRecursively() dir.mkdirs()
kotlinDir.mkdirs()
if (shouldGenerateResClass.get()) { if (shouldGenerateCode.get()) {
val rootResDir = resDir.get() logger.info("Generate $RES_FILE_NAME.kt")
logger.info("Generate resources for $rootResDir")
//get first level dirs val pkgName = packageName.get()
val dirs = rootResDir.listNotHiddenFiles() val moduleDirectory = packagingDir.getOrNull()?.let { it.invariantSeparatorsPath + "/" } ?: ""
val isPublic = makeAccessorsPublic.get()
dirs.forEach { f -> getResFileSpec(pkgName, RES_FILE_NAME, moduleDirectory, isPublic).writeTo(dir)
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) }
} else { } else {
logger.info("Generation Res class is disabled") logger.info("Generation Res class is disabled")
} }
} catch (e: Exception) { } catch (e: Exception) {
//message must contain two ':' symbols to be parsed by IDE UI! //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 return this
} }
// We need to divide accessors by different files because internal fun getResFileSpec(
//
// 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>>>,
packageName: String, packageName: String,
fileName: String,
moduleDir: String, moduleDir: String,
isPublic: Boolean isPublic: Boolean
): List<FileSpec> { ): FileSpec {
val resModifier = if (isPublic) KModifier.PUBLIC else KModifier.INTERNAL val resModifier = if (isPublic) KModifier.PUBLIC else KModifier.INTERNAL
val files = mutableListOf<FileSpec>() return FileSpec.builder(packageName, fileName).also { file ->
val resClass = FileSpec.builder(packageName, "Res").also { file ->
file.addAnnotation( file.addAnnotation(
AnnotationSpec.builder(ClassName("kotlin", "OptIn")) AnnotationSpec.builder(ClassName("kotlin", "OptIn"))
.addMember("org.jetbrains.compose.resources.InternalResourceApi::class") .addMember("org.jetbrains.compose.resources.InternalResourceApi::class")
@ -186,7 +175,27 @@ internal fun getResFileSpecs(
} }
}.build()) }.build())
}.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 //we need to sort it to generate the same code on different platforms
sortResources(resources).forEach { (type, idToResources) -> sortResources(resources).forEach { (type, idToResources) ->
@ -196,7 +205,8 @@ internal fun getResFileSpecs(
files.add( files.add(
getChunkFileSpec( getChunkFileSpec(
type, type,
index, "${type.accessorName.uppercaseFirstChar()}$index.$sourceSetName",
sourceSetName.uppercaseFirstChar() + type.accessorName.uppercaseFirstChar() + index,
packageName, packageName,
moduleDir, moduleDir,
resModifier, resModifier,
@ -211,14 +221,14 @@ internal fun getResFileSpecs(
private fun getChunkFileSpec( private fun getChunkFileSpec(
type: ResourceType, type: ResourceType,
index: Int, fileName: String,
chunkClassName: String,
packageName: String, packageName: String,
moduleDir: String, moduleDir: String,
resModifier: KModifier, resModifier: KModifier,
idToResources: Map<String, List<ResourceItem>> idToResources: Map<String, List<ResourceItem>>
): FileSpec { ): FileSpec {
val chunkClassName = type.accessorName.uppercaseFirstChar() + index return FileSpec.builder(packageName, fileName).also { chunkFile ->
return FileSpec.builder(packageName, chunkClassName).also { chunkFile ->
chunkFile.addAnnotation( chunkFile.addAnnotation(
AnnotationSpec.builder(ClassName("kotlin", "OptIn")) AnnotationSpec.builder(ClassName("kotlin", "OptIn"))
.addMember("org.jetbrains.compose.resources.InternalResourceApi::class") .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( internal fun Project.configureKmpResources(
kotlinExtension: KotlinProjectExtension, kotlinExtension: KotlinProjectExtension,
kmpResources: Any, kmpResources: Any,
preparedCommonResources: Provider<File>,
config: Provider<ResourcesExtension> config: Provider<ResourcesExtension>
) { ) {
kotlinExtension as KotlinMultiplatformExtension kotlinExtension as KotlinMultiplatformExtension
@ -23,6 +22,9 @@ internal fun Project.configureKmpResources(
logger.info("Configure KMP resources") 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 //configure KMP resources publishing for each supported target
kotlinExtension.targets kotlinExtension.targets
.matching { target -> kmpResources.canPublishResources(target) } .matching { target -> kmpResources.canPublishResources(target) }
@ -33,16 +35,8 @@ internal fun Project.configureKmpResources(
kmpResources.publishResourcesAsKotlinComponent( kmpResources.publishResourcesAsKotlinComponent(
target, target,
{ sourceSet -> { 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( KotlinTargetResourcesPublication.ResourceRoot(
composeResDir, getPreparedComposeResourcesDir(sourceSet),
emptyList(), emptyList(),
//for android target exclude fonts //for android target exclude fonts
if (target is KotlinAndroidTarget) listOf("**/font*/*") else emptyList() if (target is KotlinAndroidTarget) listOf("**/font*/*") else emptyList()
@ -57,15 +51,8 @@ internal fun Project.configureKmpResources(
kmpResources.publishInAndroidAssets( kmpResources.publishInAndroidAssets(
target, target,
{ sourceSet -> { 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( KotlinTargetResourcesPublication.ResourceRoot(
composeResDir, getPreparedComposeResourcesDir(sourceSet),
listOf("**/font*/*"), listOf("**/font*/*"),
emptyList() 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 //add all resolved resources for browser and native compilations
val platformsForSetupCompilation = listOf(KotlinPlatformType.native, KotlinPlatformType.js, KotlinPlatformType.wasm) val platformsForSetupCompilation = listOf(KotlinPlatformType.native, KotlinPlatformType.js, KotlinPlatformType.wasm)
kotlinExtension.targets 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.Property
import org.gradle.api.provider.Provider import org.gradle.api.provider.Provider
import org.gradle.api.tasks.* 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 org.w3c.dom.Node
import java.io.File import java.io.File
import java.util.* import java.util.*
@ -13,19 +15,23 @@ import javax.inject.Inject
import javax.xml.parsers.DocumentBuilderFactory import javax.xml.parsers.DocumentBuilderFactory
internal fun Project.registerPrepareComposeResourcesTask( internal fun Project.registerPrepareComposeResourcesTask(
userComposeResourcesDir: File, sourceSet: KotlinSourceSet
preparedComposeResourcesDir: Provider<Directory>
): TaskProvider<PrepareComposeResourcesTask> { ): 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( val convertXmlValueResources = tasks.register(
"convertXmlValueResources", "convertXmlValueResourcesFor${sourceSet.name.uppercaseFirstChar()}",
XmlValuesConverterTask::class.java XmlValuesConverterTask::class.java
) { task -> ) { task ->
task.fileSuffix.set(sourceSet.name)
task.originalResourcesDir.set(userComposeResourcesDir) task.originalResourcesDir.set(userComposeResourcesDir)
task.outputDir.set(preparedComposeResourcesDir) task.outputDir.set(preparedComposeResourcesDir)
} }
val copyNonXmlValueResources = tasks.register( val copyNonXmlValueResources = tasks.register(
"copyNonXmlValueResources", "copyNonXmlValueResourcesFor${sourceSet.name.uppercaseFirstChar()}",
CopyNonXmlValueResourcesTask::class.java CopyNonXmlValueResourcesTask::class.java
) { task -> ) { task ->
task.originalResourcesDir.set(userComposeResourcesDir) task.originalResourcesDir.set(userComposeResourcesDir)
@ -33,17 +39,27 @@ internal fun Project.registerPrepareComposeResourcesTask(
} }
val prepareComposeResourcesTask = tasks.register( val prepareComposeResourcesTask = tasks.register(
"prepareComposeResourcesTask", getPrepareComposeResourcesTaskName(sourceSet),
PrepareComposeResourcesTask::class.java PrepareComposeResourcesTask::class.java
) { task -> ) { task ->
task.convertedXmls.set(convertXmlValueResources.map { it.realOutputFiles.get() }) task.convertedXmls.set(convertXmlValueResources.map { it.realOutputFiles.get() })
task.copiedNonXmls.set(copyNonXmlValueResources.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 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() { internal abstract class CopyNonXmlValueResourcesTask : DefaultTask() {
@get:Inject @get:Inject
abstract val fileSystem: FileSystemOperations abstract val fileSystem: FileSystemOperations
@ -52,6 +68,7 @@ internal abstract class CopyNonXmlValueResourcesTask : DefaultTask() {
abstract val originalResourcesDir: DirectoryProperty abstract val originalResourcesDir: DirectoryProperty
@get:InputFiles @get:InputFiles
@get:SkipWhenEmpty
val realInputFiles = originalResourcesDir.map { dir -> val realInputFiles = originalResourcesDir.map { dir ->
dir.asFileTree.matching { it.exclude("values*/*.xml") } dir.asFileTree.matching { it.exclude("values*/*.xml") }
} }
@ -67,7 +84,7 @@ internal abstract class CopyNonXmlValueResourcesTask : DefaultTask() {
@TaskAction @TaskAction
fun run() { fun run() {
realOutputFiles.get().forEach { f -> f.delete() } realOutputFiles.get().forEach { f -> f.delete() }
fileSystem.copy { copy -> fileSystem.copy { copy ->
copy.includeEmptyDirs = false copy.includeEmptyDirs = false
copy.from(originalResourcesDir) { copy.from(originalResourcesDir) {
it.exclude("values*/*.xml") it.exclude("values*/*.xml")
@ -79,16 +96,18 @@ internal abstract class CopyNonXmlValueResourcesTask : DefaultTask() {
internal abstract class PrepareComposeResourcesTask : DefaultTask() { internal abstract class PrepareComposeResourcesTask : DefaultTask() {
@get:InputFiles @get:InputFiles
@get:SkipWhenEmpty
abstract val convertedXmls: Property<FileTree> abstract val convertedXmls: Property<FileTree>
@get:InputFiles @get:InputFiles
@get:SkipWhenEmpty
abstract val copiedNonXmls: Property<FileTree> abstract val copiedNonXmls: Property<FileTree>
@get:OutputDirectory @get:OutputDirectory
abstract val outputDir: Property<File> abstract val outputDir: DirectoryProperty
@TaskAction @TaskAction
fun run() {} fun run() = Unit
} }
internal data class ValueResourceRecord( internal data class ValueResourceRecord(
@ -119,10 +138,14 @@ internal abstract class XmlValuesConverterTask : DefaultTask() {
private const val FORMAT_VERSION = 0 private const val FORMAT_VERSION = 0
} }
@get:Input
abstract val fileSuffix: Property<String>
@get:Internal @get:Internal
abstract val originalResourcesDir: DirectoryProperty abstract val originalResourcesDir: DirectoryProperty
@get:InputFiles @get:InputFiles
@get:SkipWhenEmpty
val realInputFiles = originalResourcesDir.map { dir -> val realInputFiles = originalResourcesDir.map { dir ->
dir.asFileTree.matching { it.include("values*/*.xml") } dir.asFileTree.matching { it.include("values*/*.xml") }
} }
@ -132,12 +155,14 @@ internal abstract class XmlValuesConverterTask : DefaultTask() {
@get:OutputFiles @get:OutputFiles
val realOutputFiles = outputDir.map { dir -> 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 @TaskAction
fun run() { fun run() {
val outDir = outputDir.get().asFile val outDir = outputDir.get().asFile
val suffix = fileSuffix.get()
realOutputFiles.get().forEach { f -> f.delete() } realOutputFiles.get().forEach { f -> f.delete() }
originalResourcesDir.get().asFile.listNotHiddenFiles().forEach { valuesDir -> originalResourcesDir.get().asFile.listNotHiddenFiles().forEach { valuesDir ->
if (valuesDir.isDirectory && valuesDir.name.startsWith("values")) { if (valuesDir.isDirectory && valuesDir.name.startsWith("values")) {
@ -145,7 +170,7 @@ internal abstract class XmlValuesConverterTask : DefaultTask() {
if (f.extension.equals("xml", true)) { if (f.extension.equals("xml", true)) {
val output = outDir val output = outDir
.resolve(f.parentFile.name) .resolve(f.parentFile.name)
.resolve(f.nameWithoutExtension + ".$CONVERTED_RESOURCE_EXT") .resolve(f.nameWithoutExtension + ".$suffix.$CONVERTED_RESOURCE_EXT")
output.parentFile.mkdirs() output.parentFile.mkdirs()
convert(f, output) convert(f, output)
} }
@ -195,6 +220,7 @@ internal abstract class XmlValuesConverterTask : DefaultTask() {
quantity.uppercase() + ":" + content.asBase64() quantity.uppercase() + ":" + content.asBase64()
} }
} }
else -> error("Unknown string resource type: '$type'") else -> error("Unknown string resource type: '$type'")
} }
return ValueResourceRecord(type, key, value) 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 @Test
fun testGeneratedAccessors(): Unit = with(testProject("misc/commonResources")) { fun testGeneratedAccessors(): Unit = with(testProject("misc/commonResources")) {
//check generated resource's accessors //check generated resource's accessors
gradle("generateComposeResClass").checks { gradle("prepareKotlinIdeaImport").checks {
assertDirectoriesContentEquals( assertDirectoriesContentEquals(
file("build/generated/compose/resourceGenerator/kotlin/app/group/resources_test/generated/resources"), file("build/generated/compose/resourceGenerator/kotlin"),
file("expected") file("expected")
) )
} }
@ -25,17 +25,17 @@ class ResourcesTest : GradlePluginTestBase() {
file("src/commonMain/composeResources/drawable/vector_2.xml").renameTo( file("src/commonMain/composeResources/drawable/vector_2.xml").renameTo(
file("src/commonMain/composeResources/drawable/vector_3.xml") file("src/commonMain/composeResources/drawable/vector_3.xml")
) )
gradle("generateComposeResClass").checks { gradle("prepareKotlinIdeaImport").checks {
assertNotEqualTextFiles( assertNotEqualTextFiles(
file("build/generated/compose/resourceGenerator/kotlin/app/group/resources_test/generated/resources/Drawable0.kt"), file("build/generated/compose/resourceGenerator/kotlin/commonMainResourceAccessors/app/group/resources_test/generated/resources/Drawable0.commonMain.kt"),
file("expected/Drawable0.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-en").renameTo(
file("src/commonMain/composeResources/drawable-rent") file("src/commonMain/composeResources/drawable-rent")
) )
gradle("generateComposeResClass").checks { gradle("prepareKotlinIdeaImport").checks {
check.logContains( check.logContains(
""" """
contains unknown qualifier: 'rent'. contains unknown qualifier: 'rent'.
@ -46,7 +46,7 @@ class ResourcesTest : GradlePluginTestBase() {
file("src/commonMain/composeResources/drawable-rent").renameTo( file("src/commonMain/composeResources/drawable-rent").renameTo(
file("src/commonMain/composeResources/drawable-rUS-en") file("src/commonMain/composeResources/drawable-rUS-en")
) )
gradle("generateComposeResClass").checks { gradle("prepareKotlinIdeaImport").checks {
check.logContains( check.logContains(
""" """
Region qualifier must be declared after language: 'en-rUS'. 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-en").renameTo(
file("src/commonMain/composeResources/drawable-rUS") file("src/commonMain/composeResources/drawable-rUS")
) )
gradle("generateComposeResClass").checks { gradle("prepareKotlinIdeaImport").checks {
check.logContains( check.logContains(
""" """
Region qualifier must be used only with language. 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-rUS").renameTo(
file("src/commonMain/composeResources/drawable-en-fr") file("src/commonMain/composeResources/drawable-en-fr")
) )
gradle("generateComposeResClass").checks { gradle("prepareKotlinIdeaImport").checks {
check.logContains( check.logContains(
""" """
contains repetitive qualifiers: 'en' and 'fr'. 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/drawable-en-fr").renameTo(
file("src/commonMain/composeResources/image") file("src/commonMain/composeResources/image")
) )
gradle("generateComposeResClass").checks { gradle("prepareKotlinIdeaImport").checks {
check.logContains( check.logContains(
""" """
Unknown resource type: 'image' Unknown resource type: 'image'
@ -90,7 +90,7 @@ class ResourcesTest : GradlePluginTestBase() {
file("src/commonMain/composeResources/image").renameTo( file("src/commonMain/composeResources/image").renameTo(
file("src/commonMain/composeResources/files-de") file("src/commonMain/composeResources/files-de")
) )
gradle("generateComposeResClass").checks { gradle("prepareKotlinIdeaImport").checks {
check.logContains( check.logContains(
""" """
The 'files' directory doesn't support qualifiers: 'files-de'. 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/files-de").renameTo(
file("src/commonMain/composeResources/strings") file("src/commonMain/composeResources/strings")
) )
gradle("generateComposeResClass").checks { gradle("prepareKotlinIdeaImport").checks {
check.logContains( check.logContains(
""" """
Unknown resource type: 'strings'. Unknown resource type: 'strings'.
@ -112,7 +112,7 @@ class ResourcesTest : GradlePluginTestBase() {
file("src/commonMain/composeResources/strings").renameTo( file("src/commonMain/composeResources/strings").renameTo(
file("src/commonMain/composeResources/string-us") file("src/commonMain/composeResources/string-us")
) )
gradle("generateComposeResClass").checks { gradle("prepareKotlinIdeaImport").checks {
check.logContains( check.logContains(
""" """
Forbidden directory name 'string-us'! String resources should be declared in 'values/strings.xml'. Forbidden directory name 'string-us'! String resources should be declared in 'values/strings.xml'.
@ -137,9 +137,9 @@ class ResourcesTest : GradlePluginTestBase() {
""".trimIndent() """.trimIndent()
} }
gradle("generateComposeResClass").checks { gradle("prepareKotlinIdeaImport").checks {
assertDirectoriesContentEquals( assertDirectoriesContentEquals(
file("build/generated/compose/resourceGenerator/kotlin/my/lib/res"), file("build/generated/compose/resourceGenerator/kotlin"),
file("expected-open-res") file("expected-open-res")
) )
} }
@ -382,7 +382,8 @@ class ResourcesTest : GradlePluginTestBase() {
file.parentFile.name.startsWith("value") && file.parentFile.name.startsWith("value") &&
file.extension.equals("xml", true) 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 { } else {
file file
} }
@ -431,7 +432,7 @@ class ResourcesTest : GradlePluginTestBase() {
fun testUpToDateChecks(): Unit = with(testProject("misc/commonResources")) { fun testUpToDateChecks(): Unit = with(testProject("misc/commonResources")) {
gradle("prepareKotlinIdeaImport").checks { gradle("prepareKotlinIdeaImport").checks {
check.taskSuccessful(":generateComposeResClass") 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 { gradle("prepareKotlinIdeaImport").checks {
check.taskUpToDate(":generateComposeResClass") check.taskUpToDate(":generateComposeResClass")
@ -445,7 +446,7 @@ class ResourcesTest : GradlePluginTestBase() {
} }
gradle("prepareKotlinIdeaImport").checks { gradle("prepareKotlinIdeaImport").checks {
check.taskSuccessful(":generateComposeResClass") 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 -> modifyText("build.gradle.kts") { str ->
@ -462,8 +463,8 @@ class ResourcesTest : GradlePluginTestBase() {
} }
gradle("prepareKotlinIdeaImport").checks { gradle("prepareKotlinIdeaImport").checks {
check.taskSuccessful(":generateComposeResClass") 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())
assertTrue(file("build/generated/compose/resourceGenerator/kotlin/io/company/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")) { fun testEmptyResClass(): Unit = with(testProject("misc/emptyResources")) {
gradle("generateComposeResClass").checks { gradle("generateComposeResClass").checks {
assertDirectoriesContentEquals( 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") file("expected")
) )
} }
@ -479,13 +480,12 @@ class ResourcesTest : GradlePluginTestBase() {
@Test @Test
fun testJvmOnlyProject(): Unit = with(testProject("misc/jvmOnlyResources")) { fun testJvmOnlyProject(): Unit = with(testProject("misc/jvmOnlyResources")) {
gradle("generateComposeResClass").checks { gradle("jar").checks {
assertDirectoriesContentEquals( assertDirectoriesContentEquals(
file("build/generated/compose/resourceGenerator/kotlin/me/app/jvmonlyresources/generated/resources"), file("build/generated/compose/resourceGenerator/kotlin"),
file("expected") file("expected")
) )
} }
gradle("jar")
} }
//https://github.com/gmazzo/gradle-buildconfig-plugin/issues/131 //https://github.com/gmazzo/gradle-buildconfig-plugin/issues/131
@ -509,8 +509,10 @@ class ResourcesTest : GradlePluginTestBase() {
} }
val expectedFilesCount = expected.walkTopDown() val expectedFilesCount = expected.walkTopDown()
.filter { !it.isDirectory }
.map { it.toPath().relativeTo(expectedPath) }.sorted().joinToString("\n") .map { it.toPath().relativeTo(expectedPath) }.sorted().joinToString("\n")
val actualFilesCount = actual.walkTopDown() val actualFilesCount = actual.walkTopDown()
.filter { !it.isDirectory }
.map { it.toPath().relativeTo(actualPath) }.sorted().joinToString("\n") .map { it.toPath().relativeTo(actualPath) }.sorted().joinToString("\n")
assertEquals(expectedFilesCount, actualFilesCount) 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 import org.jetbrains.compose.resources.ExperimentalResourceApi
@ExperimentalResourceApi @ExperimentalResourceApi
private object Drawable0 { private object CommonMainDrawable0 {
public val _3_strange_name: DrawableResource by public val _3_strange_name: DrawableResource by
lazy { init__3_strange_name() } lazy { init__3_strange_name() }
@ -23,7 +23,7 @@ private object Drawable0 {
@ExperimentalResourceApi @ExperimentalResourceApi
public val Res.drawable._3_strange_name: DrawableResource public val Res.drawable._3_strange_name: DrawableResource
get() = Drawable0._3_strange_name get() = CommonMainDrawable0._3_strange_name
@ExperimentalResourceApi @ExperimentalResourceApi
private fun init__3_strange_name(): DrawableResource = private fun init__3_strange_name(): DrawableResource =
@ -36,7 +36,7 @@ private fun init__3_strange_name(): DrawableResource =
@ExperimentalResourceApi @ExperimentalResourceApi
public val Res.drawable.camelCaseName: DrawableResource public val Res.drawable.camelCaseName: DrawableResource
get() = Drawable0.camelCaseName get() = CommonMainDrawable0.camelCaseName
@ExperimentalResourceApi @ExperimentalResourceApi
private fun init_camelCaseName(): DrawableResource = private fun init_camelCaseName(): DrawableResource =
@ -49,7 +49,7 @@ private fun init_camelCaseName(): DrawableResource =
@ExperimentalResourceApi @ExperimentalResourceApi
public val Res.drawable.vector: DrawableResource public val Res.drawable.vector: DrawableResource
get() = Drawable0.vector get() = CommonMainDrawable0.vector
@ExperimentalResourceApi @ExperimentalResourceApi
private fun init_vector(): DrawableResource = org.jetbrains.compose.resources.DrawableResource( 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 @ExperimentalResourceApi
public val Res.drawable.vector_2: DrawableResource public val Res.drawable.vector_2: DrawableResource
get() = Drawable0.vector_2 get() = CommonMainDrawable0.vector_2
@ExperimentalResourceApi @ExperimentalResourceApi
private fun init_vector_2(): DrawableResource = org.jetbrains.compose.resources.DrawableResource( 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 import org.jetbrains.compose.resources.FontResource
@ExperimentalResourceApi @ExperimentalResourceApi
private object Font0 { private object CommonMainFont0 {
public val emptyFont: FontResource by public val emptyFont: FontResource by
lazy { init_emptyFont() } lazy { init_emptyFont() }
} }
@ExperimentalResourceApi @ExperimentalResourceApi
public val Res.font.emptyFont: FontResource public val Res.font.emptyFont: FontResource
get() = Font0.emptyFont get() = CommonMainFont0.emptyFont
@ExperimentalResourceApi @ExperimentalResourceApi
private fun init_emptyFont(): FontResource = org.jetbrains.compose.resources.FontResource( 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 import org.jetbrains.compose.resources.PluralStringResource
@ExperimentalResourceApi @ExperimentalResourceApi
private object Plurals0 { private object CommonMainPlurals0 {
public val numberOfSongsAvailable: PluralStringResource by public val numberOfSongsAvailable: PluralStringResource by
lazy { init_numberOfSongsAvailable() } lazy { init_numberOfSongsAvailable() }
} }
@ExperimentalResourceApi @ExperimentalResourceApi
public val Res.plurals.numberOfSongsAvailable: PluralStringResource public val Res.plurals.numberOfSongsAvailable: PluralStringResource
get() = Plurals0.numberOfSongsAvailable get() = CommonMainPlurals0.numberOfSongsAvailable
@ExperimentalResourceApi @ExperimentalResourceApi
private fun init_numberOfSongsAvailable(): PluralStringResource = private fun init_numberOfSongsAvailable(): PluralStringResource =
org.jetbrains.compose.resources.PluralStringResource( org.jetbrains.compose.resources.PluralStringResource(
"plurals:numberOfSongsAvailable", "numberOfSongsAvailable", "plurals:numberOfSongsAvailable", "numberOfSongsAvailable",
setOf( 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 import org.jetbrains.compose.resources.StringResource
@ExperimentalResourceApi @ExperimentalResourceApi
private object String0 { private object CommonMainString0 {
public val PascalCase: StringResource by public val PascalCase: StringResource by
lazy { init_PascalCase() } lazy { init_PascalCase() }
@ -32,84 +32,91 @@ private object String0 {
@ExperimentalResourceApi @ExperimentalResourceApi
public val Res.string.PascalCase: StringResource public val Res.string.PascalCase: StringResource
get() = String0.PascalCase get() = CommonMainString0.PascalCase
@ExperimentalResourceApi @ExperimentalResourceApi
private fun init_PascalCase(): StringResource = org.jetbrains.compose.resources.StringResource( private fun init_PascalCase(): StringResource = org.jetbrains.compose.resources.StringResource(
"string:PascalCase", "PascalCase", "string:PascalCase", "PascalCase",
setOf( 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 @ExperimentalResourceApi
public val Res.string._1_kebab_case: StringResource public val Res.string._1_kebab_case: StringResource
get() = String0._1_kebab_case get() = CommonMainString0._1_kebab_case
@ExperimentalResourceApi @ExperimentalResourceApi
private fun init__1_kebab_case(): StringResource = org.jetbrains.compose.resources.StringResource( private fun init__1_kebab_case(): StringResource = org.jetbrains.compose.resources.StringResource(
"string:_1_kebab_case", "_1_kebab_case", "string:_1_kebab_case", "_1_kebab_case",
setOf( 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 @ExperimentalResourceApi
public val Res.string.app_name: StringResource public val Res.string.app_name: StringResource
get() = String0.app_name get() = CommonMainString0.app_name
@ExperimentalResourceApi @ExperimentalResourceApi
private fun init_app_name(): StringResource = org.jetbrains.compose.resources.StringResource( private fun init_app_name(): StringResource = org.jetbrains.compose.resources.StringResource(
"string:app_name", "app_name", "string:app_name", "app_name",
setOf( 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 @ExperimentalResourceApi
public val Res.string.camelCase: StringResource public val Res.string.camelCase: StringResource
get() = String0.camelCase get() = CommonMainString0.camelCase
@ExperimentalResourceApi @ExperimentalResourceApi
private fun init_camelCase(): StringResource = org.jetbrains.compose.resources.StringResource( private fun init_camelCase(): StringResource = org.jetbrains.compose.resources.StringResource(
"string:camelCase", "camelCase", "string:camelCase", "camelCase",
setOf( 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 @ExperimentalResourceApi
public val Res.string.hello: StringResource public val Res.string.hello: StringResource
get() = String0.hello get() = CommonMainString0.hello
@ExperimentalResourceApi @ExperimentalResourceApi
private fun init_hello(): StringResource = org.jetbrains.compose.resources.StringResource( private fun init_hello(): StringResource = org.jetbrains.compose.resources.StringResource(
"string:hello", "hello", "string:hello", "hello",
setOf( 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 @ExperimentalResourceApi
public val Res.string.multi_line: StringResource public val Res.string.multi_line: StringResource
get() = String0.multi_line get() = CommonMainString0.multi_line
@ExperimentalResourceApi @ExperimentalResourceApi
private fun init_multi_line(): StringResource = org.jetbrains.compose.resources.StringResource( private fun init_multi_line(): StringResource = org.jetbrains.compose.resources.StringResource(
"string:multi_line", "multi_line", "string:multi_line", "multi_line",
setOf( 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 @ExperimentalResourceApi
public val Res.string.str_template: StringResource public val Res.string.str_template: StringResource
get() = String0.str_template get() = CommonMainString0.str_template
@ExperimentalResourceApi @ExperimentalResourceApi
private fun init_str_template(): StringResource = org.jetbrains.compose.resources.StringResource( private fun init_str_template(): StringResource = org.jetbrains.compose.resources.StringResource(
"string:str_template", "str_template", "string:str_template", "str_template",
setOf( 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 import org.jetbrains.compose.resources.ExperimentalResourceApi
@ExperimentalResourceApi @ExperimentalResourceApi
private object Drawable0 { private object CommonMainDrawable0 {
public val _3_strange_name: DrawableResource by public val _3_strange_name: DrawableResource by
lazy { init__3_strange_name() } lazy { init__3_strange_name() }
@ -23,7 +23,7 @@ private object Drawable0 {
@ExperimentalResourceApi @ExperimentalResourceApi
internal val Res.drawable._3_strange_name: DrawableResource internal val Res.drawable._3_strange_name: DrawableResource
get() = Drawable0._3_strange_name get() = CommonMainDrawable0._3_strange_name
@ExperimentalResourceApi @ExperimentalResourceApi
private fun init__3_strange_name(): DrawableResource = private fun init__3_strange_name(): DrawableResource =
@ -36,7 +36,7 @@ private fun init__3_strange_name(): DrawableResource =
@ExperimentalResourceApi @ExperimentalResourceApi
internal val Res.drawable.camelCaseName: DrawableResource internal val Res.drawable.camelCaseName: DrawableResource
get() = Drawable0.camelCaseName get() = CommonMainDrawable0.camelCaseName
@ExperimentalResourceApi @ExperimentalResourceApi
private fun init_camelCaseName(): DrawableResource = private fun init_camelCaseName(): DrawableResource =
@ -49,7 +49,7 @@ private fun init_camelCaseName(): DrawableResource =
@ExperimentalResourceApi @ExperimentalResourceApi
internal val Res.drawable.vector: DrawableResource internal val Res.drawable.vector: DrawableResource
get() = Drawable0.vector get() = CommonMainDrawable0.vector
@ExperimentalResourceApi @ExperimentalResourceApi
private fun init_vector(): DrawableResource = org.jetbrains.compose.resources.DrawableResource( 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 @ExperimentalResourceApi
internal val Res.drawable.vector_2: DrawableResource internal val Res.drawable.vector_2: DrawableResource
get() = Drawable0.vector_2 get() = CommonMainDrawable0.vector_2
@ExperimentalResourceApi @ExperimentalResourceApi
private fun init_vector_2(): DrawableResource = org.jetbrains.compose.resources.DrawableResource( 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 import org.jetbrains.compose.resources.FontResource
@ExperimentalResourceApi @ExperimentalResourceApi
private object Font0 { private object CommonMainFont0 {
public val emptyFont: FontResource by public val emptyFont: FontResource by
lazy { init_emptyFont() } lazy { init_emptyFont() }
} }
@ExperimentalResourceApi @ExperimentalResourceApi
internal val Res.font.emptyFont: FontResource internal val Res.font.emptyFont: FontResource
get() = Font0.emptyFont get() = CommonMainFont0.emptyFont
@ExperimentalResourceApi @ExperimentalResourceApi
private fun init_emptyFont(): FontResource = org.jetbrains.compose.resources.FontResource( 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 import org.jetbrains.compose.resources.PluralStringResource
@ExperimentalResourceApi @ExperimentalResourceApi
private object Plurals0 { private object CommonMainPlurals0 {
public val numberOfSongsAvailable: PluralStringResource by public val numberOfSongsAvailable: PluralStringResource by
lazy { init_numberOfSongsAvailable() } lazy { init_numberOfSongsAvailable() }
} }
@ExperimentalResourceApi @ExperimentalResourceApi
internal val Res.plurals.numberOfSongsAvailable: PluralStringResource internal val Res.plurals.numberOfSongsAvailable: PluralStringResource
get() = Plurals0.numberOfSongsAvailable get() = CommonMainPlurals0.numberOfSongsAvailable
@ExperimentalResourceApi @ExperimentalResourceApi
private fun init_numberOfSongsAvailable(): PluralStringResource = private fun init_numberOfSongsAvailable(): PluralStringResource =
org.jetbrains.compose.resources.PluralStringResource( org.jetbrains.compose.resources.PluralStringResource(
"plurals:numberOfSongsAvailable", "numberOfSongsAvailable", "plurals:numberOfSongsAvailable", "numberOfSongsAvailable",
setOf( 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 import org.jetbrains.compose.resources.StringResource
@ExperimentalResourceApi @ExperimentalResourceApi
private object String0 { private object CommonMainString0 {
public val PascalCase: StringResource by public val PascalCase: StringResource by
lazy { init_PascalCase() } lazy { init_PascalCase() }
@ -32,84 +32,91 @@ private object String0 {
@ExperimentalResourceApi @ExperimentalResourceApi
internal val Res.string.PascalCase: StringResource internal val Res.string.PascalCase: StringResource
get() = String0.PascalCase get() = CommonMainString0.PascalCase
@ExperimentalResourceApi @ExperimentalResourceApi
private fun init_PascalCase(): StringResource = org.jetbrains.compose.resources.StringResource( private fun init_PascalCase(): StringResource = org.jetbrains.compose.resources.StringResource(
"string:PascalCase", "PascalCase", "string:PascalCase", "PascalCase",
setOf( 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 @ExperimentalResourceApi
internal val Res.string._1_kebab_case: StringResource internal val Res.string._1_kebab_case: StringResource
get() = String0._1_kebab_case get() = CommonMainString0._1_kebab_case
@ExperimentalResourceApi @ExperimentalResourceApi
private fun init__1_kebab_case(): StringResource = org.jetbrains.compose.resources.StringResource( private fun init__1_kebab_case(): StringResource = org.jetbrains.compose.resources.StringResource(
"string:_1_kebab_case", "_1_kebab_case", "string:_1_kebab_case", "_1_kebab_case",
setOf( 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 @ExperimentalResourceApi
internal val Res.string.app_name: StringResource internal val Res.string.app_name: StringResource
get() = String0.app_name get() = CommonMainString0.app_name
@ExperimentalResourceApi @ExperimentalResourceApi
private fun init_app_name(): StringResource = org.jetbrains.compose.resources.StringResource( private fun init_app_name(): StringResource = org.jetbrains.compose.resources.StringResource(
"string:app_name", "app_name", "string:app_name", "app_name",
setOf( 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 @ExperimentalResourceApi
internal val Res.string.camelCase: StringResource internal val Res.string.camelCase: StringResource
get() = String0.camelCase get() = CommonMainString0.camelCase
@ExperimentalResourceApi @ExperimentalResourceApi
private fun init_camelCase(): StringResource = org.jetbrains.compose.resources.StringResource( private fun init_camelCase(): StringResource = org.jetbrains.compose.resources.StringResource(
"string:camelCase", "camelCase", "string:camelCase", "camelCase",
setOf( 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 @ExperimentalResourceApi
internal val Res.string.hello: StringResource internal val Res.string.hello: StringResource
get() = String0.hello get() = CommonMainString0.hello
@ExperimentalResourceApi @ExperimentalResourceApi
private fun init_hello(): StringResource = org.jetbrains.compose.resources.StringResource( private fun init_hello(): StringResource = org.jetbrains.compose.resources.StringResource(
"string:hello", "hello", "string:hello", "hello",
setOf( 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 @ExperimentalResourceApi
internal val Res.string.multi_line: StringResource internal val Res.string.multi_line: StringResource
get() = String0.multi_line get() = CommonMainString0.multi_line
@ExperimentalResourceApi @ExperimentalResourceApi
private fun init_multi_line(): StringResource = org.jetbrains.compose.resources.StringResource( private fun init_multi_line(): StringResource = org.jetbrains.compose.resources.StringResource(
"string:multi_line", "multi_line", "string:multi_line", "multi_line",
setOf( 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 @ExperimentalResourceApi
internal val Res.string.str_template: StringResource internal val Res.string.str_template: StringResource
get() = String0.str_template get() = CommonMainString0.str_template
@ExperimentalResourceApi @ExperimentalResourceApi
private fun init_str_template(): StringResource = org.jetbrains.compose.resources.StringResource( private fun init_str_template(): StringResource = org.jetbrains.compose.resources.StringResource(
"string:str_template", "str_template", "string:str_template", "str_template",
setOf( 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 import org.jetbrains.compose.resources.ExperimentalResourceApi
@ExperimentalResourceApi @ExperimentalResourceApi
private object Drawable0 { private object MainDrawable0 {
public val vector: DrawableResource by public val vector: DrawableResource by
lazy { init_vector() } lazy { init_vector() }
} }
@ExperimentalResourceApi @ExperimentalResourceApi
internal val Res.drawable.vector: DrawableResource internal val Res.drawable.vector: DrawableResource
get() = Drawable0.vector get() = MainDrawable0.vector
@ExperimentalResourceApi @ExperimentalResourceApi
private fun init_vector(): DrawableResource = org.jetbrains.compose.resources.DrawableResource( 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) MyLibraryText(txt = txt)
MyLibraryIcon(Modifier) 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