diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/ComposePlugin.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/ComposePlugin.kt index 14b9c62991..f8c35990f0 100644 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/ComposePlugin.kt +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/ComposePlugin.kt @@ -28,6 +28,7 @@ import org.jetbrains.compose.internal.mppExtOrNull import org.jetbrains.compose.internal.service.ConfigurationProblemReporterService import org.jetbrains.compose.internal.service.GradlePropertySnapshotService import org.jetbrains.compose.internal.utils.currentTarget +import org.jetbrains.compose.resources.ResourcesExtension import org.jetbrains.compose.resources.configureComposeResources import org.jetbrains.compose.resources.ios.configureSyncTask import org.jetbrains.compose.web.WebExtension @@ -52,6 +53,7 @@ abstract class ComposePlugin : Plugin { val desktopExtension = composeExtension.extensions.create("desktop", DesktopExtension::class.java) val androidExtension = composeExtension.extensions.create("android", AndroidExtension::class.java) val experimentalExtension = composeExtension.extensions.create("experimental", ExperimentalExtension::class.java) + val resourcesExtension = composeExtension.extensions.create("resources", ResourcesExtension::class.java) project.dependencies.extensions.add("compose", Dependencies(project)) @@ -65,7 +67,7 @@ abstract class ComposePlugin : Plugin { project.plugins.apply(ComposeCompilerKotlinSupportPlugin::class.java) project.configureNativeCompilerCaching() - project.configureComposeResources() + project.configureComposeResources(resourcesExtension) project.afterEvaluate { configureDesktop(project, desktopExtension) diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/GenerateResClassTask.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/GenerateResClassTask.kt index e2abbb5fd3..79410c0971 100644 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/GenerateResClassTask.kt +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/GenerateResClassTask.kt @@ -23,6 +23,9 @@ internal abstract class GenerateResClassTask : DefaultTask() { @get:Input abstract val shouldGenerateResClass: Property + @get:Input + abstract val makeResClassPublic: Property + @get:InputFiles @get:PathSensitive(PathSensitivity.RELATIVE) abstract val resDir: Property @@ -63,7 +66,8 @@ internal abstract class GenerateResClassTask : DefaultTask() { getResFileSpecs( resources, packageName.get(), - moduleDir.getOrNull()?.let { it.invariantSeparatorsPath + "/" } ?: "" + moduleDir.getOrNull()?.let { it.invariantSeparatorsPath + "/" } ?: "", + makeResClassPublic.get() ).forEach { it.writeTo(kotlinDir) } } else { logger.info("Generation Res class is disabled") diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ResourcesExtension.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ResourcesExtension.kt new file mode 100644 index 0000000000..7a83173e84 --- /dev/null +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ResourcesExtension.kt @@ -0,0 +1,33 @@ +package org.jetbrains.compose.resources + +abstract class ResourcesExtension { + /** + * Whether the generated resources accessors class should be public or not. + * + * Default is false. + */ + var publicResClass: Boolean = false + + /** + * The unique identifier of the resources in the current project. + * Uses as package for the generated Res class and for isolation resources in a final artefact. + * + * If it is empty then `{group name}.{module name}.generated.resources` will be used. + * + */ + var packageOfResClass: String = "" + + enum class ResourceClassGeneration { Auto, Always } + + //to support groovy DSL + val auto = ResourceClassGeneration.Auto + val always = ResourceClassGeneration.Always + + /** + * The mode of resource class generation. + * + * - `auto`: The Res class will be generated if the current project has an explicit "implementation" or "api" dependency on the resource's library. + * - `always`: Unconditionally generate the Res class. This may be useful when the resources library is available transitively. + */ + var generateResClass: ResourceClassGeneration = auto +} \ No newline at end of file diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ResourcesGenerator.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ResourcesGenerator.kt index 4156068400..7715b6868c 100644 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ResourcesGenerator.kt +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ResourcesGenerator.kt @@ -38,14 +38,20 @@ private val androidPluginIds = listOf( "com.android.library" ) -internal fun Project.configureComposeResources() { - val projectId = provider { - val groupName = project.group.toString().lowercase().asUnderscoredIdentifier() - val moduleName = project.name.lowercase().asUnderscoredIdentifier() - if (groupName.isNotEmpty()) "$groupName.$moduleName" - else moduleName +internal fun Project.configureComposeResources(config: ResourcesExtension) { + val resourcePackage = provider { + config.packageOfResClass.takeIf { it.isNotEmpty() } ?: run { + val groupName = project.group.toString().lowercase().asUnderscoredIdentifier() + val moduleName = project.name.lowercase().asUnderscoredIdentifier() + val id = if (groupName.isNotEmpty()) "$groupName.$moduleName" else moduleName + "$id.generated.resources" + } } + val publicResClass = provider { config.publicResClass } + + val generateResClassMode = provider { config.generateResClass } + plugins.withId(KOTLIN_MPP_PLUGIN_ID) { val kotlinExtension = project.extensions.getByType(KotlinMultiplatformExtension::class.java) @@ -53,7 +59,13 @@ internal fun Project.configureComposeResources() { val currentGradleVersion = GradleVersion.current() val minGradleVersion = GradleVersion.version(MIN_GRADLE_VERSION_FOR_KMP_RESOURCES) if (hasKmpResources && currentGradleVersion >= minGradleVersion) { - configureKmpResources(kotlinExtension, extraProperties.get(KMP_RES_EXT)!!, projectId) + configureKmpResources( + kotlinExtension, + extraProperties.get(KMP_RES_EXT)!!, + resourcePackage, + publicResClass, + generateResClassMode + ) } else { if (!hasKmpResources) { logger.info( @@ -73,7 +85,13 @@ internal fun Project.configureComposeResources() { } //current KGP doesn't have KPM resources - configureComposeResources(kotlinExtension, KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME, projectId) + configureComposeResources( + kotlinExtension, + KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME, + resourcePackage, + publicResClass, + generateResClassMode + ) //when applied AGP then configure android resources androidPluginIds.forEach { pluginId -> @@ -86,14 +104,22 @@ internal fun Project.configureComposeResources() { } plugins.withId(KOTLIN_JVM_PLUGIN_ID) { val kotlinExtension = project.extensions.getByType(KotlinProjectExtension::class.java) - configureComposeResources(kotlinExtension, SourceSet.MAIN_SOURCE_SET_NAME, projectId) + configureComposeResources( + kotlinExtension, + SourceSet.MAIN_SOURCE_SET_NAME, + resourcePackage, + publicResClass, + generateResClassMode + ) } } private fun Project.configureComposeResources( kotlinExtension: KotlinProjectExtension, commonSourceSetName: String, - projectId: Provider + resourcePackage: Provider, + publicResClass: Provider, + generateResClassMode: Provider ) { logger.info("Configure compose resources") kotlinExtension.sourceSets.all { sourceSet -> @@ -105,7 +131,14 @@ private fun Project.configureComposeResources( sourceSet.resources.srcDirs(composeResourcesPath) if (sourceSetName == commonSourceSetName) { - configureResourceGenerator(composeResourcesPath, sourceSet, projectId, false) + configureResourceGenerator( + composeResourcesPath, + sourceSet, + resourcePackage, + publicResClass, + generateResClassMode, + false + ) } } } @@ -114,7 +147,9 @@ private fun Project.configureComposeResources( private fun Project.configureKmpResources( kotlinExtension: KotlinProjectExtension, kmpResources: Any, - projectId: Provider + resourcePackage: Provider, + publicResClass: Provider, + generateResClassMode: Provider ) { kotlinExtension as KotlinMultiplatformExtension kmpResources as KotlinTargetResourcesPublication @@ -136,7 +171,7 @@ private fun Project.configureKmpResources( if (target is KotlinAndroidTarget) listOf("**/font*/*") else emptyList() ) }, - projectId.asModuleDir() + resourcePackage.asModuleDir() ) if (target is KotlinAndroidTarget) { @@ -151,7 +186,7 @@ private fun Project.configureKmpResources( emptyList() ) }, - projectId.asModuleDir() + resourcePackage.asModuleDir() ) } } @@ -161,7 +196,14 @@ private fun Project.configureKmpResources( val sourceSetName = sourceSet.name if (sourceSetName == KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME) { val composeResourcesPath = project.projectDir.resolve("src/$sourceSetName/$COMPOSE_RESOURCES_DIR") - configureResourceGenerator(composeResourcesPath, sourceSet, projectId, true) + configureResourceGenerator( + composeResourcesPath, + sourceSet, + resourcePackage, + publicResClass, + generateResClassMode, + true + ) } } @@ -251,27 +293,35 @@ private fun Project.configureAndroidComposeResources( private fun Project.configureResourceGenerator( commonComposeResourcesDir: File, commonSourceSet: KotlinSourceSet, - projectId: Provider, + resourcePackage: Provider, + publicResClass: Provider, + generateResClassMode: Provider, generateModulePath: Boolean ) { - val packageName = projectId.map { "$it.generated.resources" } - logger.info("Configure accessors for '${commonSourceSet.name}'") fun buildDir(path: String) = layout.dir(layout.buildDirectory.map { File(it.asFile, path) }) //lazy check a dependency on the Resources library - val shouldGenerateResClass: Provider = provider { - if (ComposeProperties.alwaysGenerateResourceAccessors(project).get()) { - true - } else { - 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 + val shouldGenerateResClass = generateResClassMode.map { mode -> + when (mode) { + ResourcesExtension.ResourceClassGeneration.Auto -> { + //todo remove the gradle property when the gradle plugin will be published + if (ComposeProperties.alwaysGenerateResourceAccessors(project).get()) { + true + } else { + 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 } } } @@ -280,13 +330,14 @@ private fun Project.configureResourceGenerator( "generateComposeResClass", GenerateResClassTask::class.java ) { task -> - task.packageName.set(packageName) + task.packageName.set(resourcePackage) task.shouldGenerateResClass.set(shouldGenerateResClass) + task.makeResClassPublic.set(publicResClass) task.resDir.set(commonComposeResourcesDir) task.codeDir.set(buildDir("$RES_GEN_DIR/kotlin")) if (generateModulePath) { - task.moduleDir.set(projectId.asModuleDir()) + task.moduleDir.set(resourcePackage.asModuleDir()) } } diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ResourcesSpec.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ResourcesSpec.kt index 100291d866..6064e1f8c1 100644 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ResourcesSpec.kt +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ResourcesSpec.kt @@ -117,8 +117,10 @@ internal fun getResFileSpecs( //type -> id -> items resources: Map>>, packageName: String, - moduleDir: String + moduleDir: String, + isPublic: Boolean ): List { + val resModifier = if (isPublic) KModifier.PUBLIC else KModifier.INTERNAL val files = mutableListOf() val resClass = FileSpec.builder(packageName, "Res").also { file -> file.addAnnotation( @@ -128,7 +130,7 @@ internal fun getResFileSpecs( .build() ) file.addType(TypeSpec.objectBuilder("Res").also { resObject -> - resObject.addModifiers(KModifier.INTERNAL) + resObject.addModifiers(resModifier) resObject.addAnnotation(experimentalAnnotation) //readFileBytes @@ -169,6 +171,7 @@ internal fun getResFileSpecs( index, packageName, moduleDir, + resModifier, idToResources.subMap(ids.first(), true, ids.last(), true) ) ) @@ -183,6 +186,7 @@ private fun getChunkFileSpec( index: Int, packageName: String, moduleDir: String, + resModifier: KModifier, idToResources: Map> ): FileSpec { val chunkClassName = type.typeName.uppercaseFirstChar() + index @@ -206,7 +210,7 @@ private fun getChunkFileSpec( chunkFile.addType(objectSpec) idToResources.forEach { (resName, items) -> - val accessor = PropertySpec.builder(resName, type.getClassName(), KModifier.INTERNAL) + val accessor = PropertySpec.builder(resName, type.getClassName(), resModifier) .receiver(ClassName(packageName, "Res", type.typeName)) .addAnnotation(experimentalAnnotation) .getter(FunSpec.getterBuilder().addStatement("return $chunkClassName.$resName").build()) diff --git a/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt b/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt index 50a9fe813e..b1dc8a59f6 100644 --- a/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt +++ b/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt @@ -126,10 +126,20 @@ class ResourcesTest : GradlePluginTestBase() { file("src/commonMain/composeResources/drawable/vector_3.xml").renameTo( file("src/commonMain/composeResources/drawable/vector_2.xml") ) + + file("build.gradle.kts").modify { txt -> + txt + """ + compose.resources { + publicResClass = true + packageOfResClass = "my.lib.res" + } + """.trimIndent() + } + gradle("generateComposeResClass").checks { assertDirectoriesContentEquals( - file("build/generated/compose/resourceGenerator/kotlin/app/group/resources_test/generated/resources"), - file("expected") + file("build/generated/compose/resourceGenerator/kotlin/my/lib/res"), + file("expected-open-res") ) } } @@ -155,7 +165,7 @@ class ResourcesTest : GradlePluginTestBase() { val resourcesFiles = resDir.walkTopDown() .filter { !it.isDirectory && !it.isHidden } .map { it.relativeTo(resDir).invariantSeparatorsPath } - val subdir = "me.sample.library.cmplib" + val subdir = "me.sample.library.resources" fun libpath(target: String, ext: String) = "my-mvn/me/sample/library/cmplib-$target/1.0/cmplib-$target-1.0$ext" diff --git a/gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected-open-res/Drawable0.kt b/gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected-open-res/Drawable0.kt new file mode 100644 index 0000000000..ff718ae836 --- /dev/null +++ b/gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected-open-res/Drawable0.kt @@ -0,0 +1,84 @@ +@file:OptIn(org.jetbrains.compose.resources.InternalResourceApi::class) + +package my.lib.res + +import kotlin.OptIn +import org.jetbrains.compose.resources.DrawableResource +import org.jetbrains.compose.resources.ExperimentalResourceApi + +@ExperimentalResourceApi +private object Drawable0 { + public val _3_strange_name: DrawableResource by + lazy { init__3_strange_name() } + + public val camelCaseName: DrawableResource by + lazy { init_camelCaseName() } + + public val vector: DrawableResource by + lazy { init_vector() } + + public val vector_2: DrawableResource by + lazy { init_vector_2() } +} + +@ExperimentalResourceApi +public val Res.drawable._3_strange_name: DrawableResource + get() = Drawable0._3_strange_name + +@ExperimentalResourceApi +private fun init__3_strange_name(): DrawableResource = + org.jetbrains.compose.resources.DrawableResource( + "drawable:_3_strange_name", + setOf( + org.jetbrains.compose.resources.ResourceItem(setOf(), "drawable/3-strange-name.xml"), + ) +) + +@ExperimentalResourceApi +public val Res.drawable.camelCaseName: DrawableResource + get() = Drawable0.camelCaseName + +@ExperimentalResourceApi +private fun init_camelCaseName(): DrawableResource = + org.jetbrains.compose.resources.DrawableResource( + "drawable:camelCaseName", + setOf( + org.jetbrains.compose.resources.ResourceItem(setOf(), "drawable/camelCaseName.xml"), + ) +) + +@ExperimentalResourceApi +public val Res.drawable.vector: DrawableResource + get() = Drawable0.vector + +@ExperimentalResourceApi +private fun init_vector(): DrawableResource = org.jetbrains.compose.resources.DrawableResource( + "drawable:vector", + setOf( + + org.jetbrains.compose.resources.ResourceItem(setOf(org.jetbrains.compose.resources.LanguageQualifier("ast"), + ), "drawable-ast/vector.xml"), + + org.jetbrains.compose.resources.ResourceItem(setOf(org.jetbrains.compose.resources.LanguageQualifier("au"), + org.jetbrains.compose.resources.RegionQualifier("US"), ), "drawable-au-rUS/vector.xml"), + + org.jetbrains.compose.resources.ResourceItem(setOf(org.jetbrains.compose.resources.ThemeQualifier.DARK, + org.jetbrains.compose.resources.LanguageQualifier("ge"), ), "drawable-dark-ge/vector.xml"), + + org.jetbrains.compose.resources.ResourceItem(setOf(org.jetbrains.compose.resources.LanguageQualifier("en"), + ), "drawable-en/vector.xml"), + org.jetbrains.compose.resources.ResourceItem(setOf(), "drawable/vector.xml"), + ) +) + +@ExperimentalResourceApi +public val Res.drawable.vector_2: DrawableResource + get() = Drawable0.vector_2 + +@ExperimentalResourceApi +private fun init_vector_2(): DrawableResource = org.jetbrains.compose.resources.DrawableResource( + "drawable:vector_2", + setOf( + org.jetbrains.compose.resources.ResourceItem(setOf(), "drawable/vector_2.xml"), + ) +) diff --git a/gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected-open-res/Font0.kt b/gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected-open-res/Font0.kt new file mode 100644 index 0000000000..d0e70a049c --- /dev/null +++ b/gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected-open-res/Font0.kt @@ -0,0 +1,28 @@ +@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.FontResource + +@ExperimentalResourceApi +private object Font0 { + public val emptyFont: FontResource by + lazy { init_emptyFont() } +} + +@ExperimentalResourceApi +public val Res.font.emptyFont: FontResource + get() = Font0.emptyFont + +@ExperimentalResourceApi +private fun init_emptyFont(): FontResource = org.jetbrains.compose.resources.FontResource( + "font:emptyFont", + setOf( + + org.jetbrains.compose.resources.ResourceItem(setOf(org.jetbrains.compose.resources.LanguageQualifier("en"), + ), "font-en/emptyFont.otf"), + org.jetbrains.compose.resources.ResourceItem(setOf(), "font/emptyFont.otf"), + ) +) diff --git a/gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected-open-res/Res.kt b/gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected-open-res/Res.kt new file mode 100644 index 0000000000..76964658a9 --- /dev/null +++ b/gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected-open-res/Res.kt @@ -0,0 +1,31 @@ +@file:OptIn( + org.jetbrains.compose.resources.InternalResourceApi::class, + org.jetbrains.compose.resources.ExperimentalResourceApi::class, +) + +package my.lib.res + +import kotlin.ByteArray +import kotlin.OptIn +import kotlin.String +import org.jetbrains.compose.resources.ExperimentalResourceApi +import org.jetbrains.compose.resources.readResourceBytes + +@ExperimentalResourceApi +public object Res { + /** + * Reads the content of the resource file at the specified path and returns it as a byte array. + * + * Example: `val bytes = Res.readBytes("files/key.bin")` + * + * @param path The path of the file to read in the compose resource's directory. + * @return The content of the file as a byte array. + */ + public suspend fun readBytes(path: String): ByteArray = readResourceBytes("" + path) + + public object drawable + + public object string + + public object font +} diff --git a/gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected-open-res/String0.kt b/gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected-open-res/String0.kt new file mode 100644 index 0000000000..e3f5324347 --- /dev/null +++ b/gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected-open-res/String0.kt @@ -0,0 +1,130 @@ +@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 String0 { + public val PascalCase: StringResource by + lazy { init_PascalCase() } + + public val _1_kebab_case: StringResource by + lazy { init__1_kebab_case() } + + public val app_name: StringResource by + lazy { init_app_name() } + + public val camelCase: StringResource by + lazy { init_camelCase() } + + public val hello: StringResource by + lazy { init_hello() } + + public val multi_line: StringResource by + lazy { init_multi_line() } + + public val str_arr: StringResource by + lazy { init_str_arr() } + + public val str_template: StringResource by + lazy { init_str_template() } +} + +@ExperimentalResourceApi +public val Res.string.PascalCase: StringResource + get() = String0.PascalCase + +@ExperimentalResourceApi +private fun init_PascalCase(): StringResource = org.jetbrains.compose.resources.StringResource( + "string:PascalCase", "PascalCase", + setOf( + org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.xml"), + ) +) + +@ExperimentalResourceApi +public val Res.string._1_kebab_case: StringResource + get() = String0._1_kebab_case + +@ExperimentalResourceApi +private fun init__1_kebab_case(): StringResource = org.jetbrains.compose.resources.StringResource( + "string:_1_kebab_case", "_1_kebab_case", + setOf( + org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.xml"), + ) +) + +@ExperimentalResourceApi +public val Res.string.app_name: StringResource + get() = String0.app_name + +@ExperimentalResourceApi +private fun init_app_name(): StringResource = org.jetbrains.compose.resources.StringResource( + "string:app_name", "app_name", + setOf( + org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.xml"), + ) +) + +@ExperimentalResourceApi +public val Res.string.camelCase: StringResource + get() = String0.camelCase + +@ExperimentalResourceApi +private fun init_camelCase(): StringResource = org.jetbrains.compose.resources.StringResource( + "string:camelCase", "camelCase", + setOf( + org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.xml"), + ) +) + +@ExperimentalResourceApi +public val Res.string.hello: StringResource + get() = String0.hello + +@ExperimentalResourceApi +private fun init_hello(): StringResource = org.jetbrains.compose.resources.StringResource( + "string:hello", "hello", + setOf( + org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.xml"), + ) +) + +@ExperimentalResourceApi +public val Res.string.multi_line: StringResource + get() = String0.multi_line + +@ExperimentalResourceApi +private fun init_multi_line(): StringResource = org.jetbrains.compose.resources.StringResource( + "string:multi_line", "multi_line", + setOf( + org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.xml"), + ) +) + +@ExperimentalResourceApi +public val Res.string.str_arr: StringResource + get() = String0.str_arr + +@ExperimentalResourceApi +private fun init_str_arr(): StringResource = org.jetbrains.compose.resources.StringResource( + "string:str_arr", "str_arr", + setOf( + org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.xml"), + ) +) + +@ExperimentalResourceApi +public val Res.string.str_template: StringResource + get() = String0.str_template + +@ExperimentalResourceApi +private fun init_str_template(): StringResource = org.jetbrains.compose.resources.StringResource( + "string:str_template", "str_template", + setOf( + org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.xml"), + ) +) diff --git a/gradle-plugins/compose/src/test/test-projects/misc/kmpResourcePublication/appModule/src/commonTest/kotlin/ComposeAppTest.kt b/gradle-plugins/compose/src/test/test-projects/misc/kmpResourcePublication/appModule/src/commonTest/kotlin/ComposeAppTest.kt index 8400c2df29..b2241d0d62 100644 --- a/gradle-plugins/compose/src/test/test-projects/misc/kmpResourcePublication/appModule/src/commonTest/kotlin/ComposeAppTest.kt +++ b/gradle-plugins/compose/src/test/test-projects/misc/kmpResourcePublication/appModule/src/commonTest/kotlin/ComposeAppTest.kt @@ -11,6 +11,8 @@ import me.sample.app.MyFeatureText import me.sample.library.MyLibraryText import org.jetbrains.compose.resources.stringResource import kmpresourcepublication.appmodule.generated.resources.* +import me.sample.library.resources.Res as LibRes +import me.sample.library.resources.* import kotlin.test.Test @OptIn(ExperimentalTestApi::class) @@ -28,11 +30,18 @@ class ComposeAppTest { ) MyFeatureText(Modifier.testTag("feature-text"), txt) MyLibraryText(Modifier.testTag("library-text"), txt) + + //direct read a resource from library + Text( + modifier = Modifier.testTag("library-resource-text"), + text = stringResource(LibRes.string.str_1) + ) } } onNodeWithTag("app-text").assertTextEquals("test text: App text str_1") onNodeWithTag("feature-text").assertTextEquals("test text: Feature text str_1") onNodeWithTag("library-text").assertTextEquals("test text: Library text str_1") + onNodeWithTag("library-resource-text").assertTextEquals("Library text str_1") } } \ No newline at end of file diff --git a/gradle-plugins/compose/src/test/test-projects/misc/kmpResourcePublication/cmplib/build.gradle.kts b/gradle-plugins/compose/src/test/test-projects/misc/kmpResourcePublication/cmplib/build.gradle.kts index fde47155a5..e949b0f928 100644 --- a/gradle-plugins/compose/src/test/test-projects/misc/kmpResourcePublication/cmplib/build.gradle.kts +++ b/gradle-plugins/compose/src/test/test-projects/misc/kmpResourcePublication/cmplib/build.gradle.kts @@ -53,4 +53,9 @@ android { compose { kotlinCompilerPlugin.set(dependencies.compiler.forKotlin("COMPOSE_COMPILER_PLUGIN_PLACEHOLDER")) kotlinCompilerPluginArgs.add("suppressKotlinVersionCompatibilityCheck=KOTLIN_VERSION_PLACEHOLDER") +} + +compose.resources { + publicResClass = true + packageOfResClass = "me.sample.library.resources" } \ No newline at end of file diff --git a/gradle-plugins/compose/src/test/test-projects/misc/kmpResourcePublication/cmplib/src/commonMain/kotlin/me/sample/library/Lib.kt b/gradle-plugins/compose/src/test/test-projects/misc/kmpResourcePublication/cmplib/src/commonMain/kotlin/me/sample/library/Lib.kt index 4318c76854..e041e4d2e2 100644 --- a/gradle-plugins/compose/src/test/test-projects/misc/kmpResourcePublication/cmplib/src/commonMain/kotlin/me/sample/library/Lib.kt +++ b/gradle-plugins/compose/src/test/test-projects/misc/kmpResourcePublication/cmplib/src/commonMain/kotlin/me/sample/library/Lib.kt @@ -6,7 +6,7 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontFamily -import me.sample.library.cmplib.generated.resources.* +import me.sample.library.resources.* import org.jetbrains.compose.resources.Font import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.stringResource