Skip to content

Commit e6b29f8

Browse files
terrakokKpotko
authored andcommitted
Refactor resource accessors generator to minify output source files. (#5298)
Refactor resource accessors generator to minify output source files. - Number of items per file was decreased to 100 - Common part of the path extracted to the private const - Added imports - Removed the private object with item's properties - The file structure was reorganized Fixes [CMP-7934](https://youtrack.jetbrains.com/issue/CMP-7934) Too large String0.commonMain.kt will break code insight feature in AS ## Testing Integration tests. This should be tested by QA ## Release Notes ### Fixes - Resources - Fix IDE highlighting/resoulution when a generated file with resource accessors is too big (cherry picked from commit 1b6e615)
1 parent 062f12f commit e6b29f8

File tree

28 files changed

+19328
-589
lines changed

28 files changed

+19328
-589
lines changed

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

Lines changed: 43 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,8 @@ private fun ResourceType.requiresKeyName() =
5555
this in setOf(ResourceType.STRING, ResourceType.STRING_ARRAY, ResourceType.PLURAL_STRING)
5656

5757
private val resourceItemClass = ClassName("org.jetbrains.compose.resources", "ResourceItem")
58-
private val internalAnnotation = AnnotationSpec.builder(
59-
ClassName("org.jetbrains.compose.resources", "InternalResourceApi")
60-
).build()
58+
private val internalAnnotationClass = ClassName("org.jetbrains.compose.resources", "InternalResourceApi")
59+
private val internalAnnotation = AnnotationSpec.builder(internalAnnotationClass).build()
6160

6261
private fun CodeBlock.Builder.addQualifiers(resourceItem: ResourceItem): CodeBlock.Builder {
6362
val languageQualifier = ClassName("org.jetbrains.compose.resources", "LanguageQualifier")
@@ -134,13 +133,13 @@ internal fun getResFileSpec(
134133
return FileSpec.builder(packageName, fileName).also { file ->
135134
file.addAnnotation(
136135
AnnotationSpec.builder(ClassName("kotlin", "OptIn"))
137-
.addMember("org.jetbrains.compose.resources.InternalResourceApi::class")
136+
.addMember("%T::class", internalAnnotationClass)
138137
.build()
139138
)
140139
file.addAnnotation(
141140
AnnotationSpec.builder(ClassName("kotlin", "Suppress"))
142-
.addMember("%S","RedundantVisibilityModifier")
143-
.addMember("%S","REDUNDANT_VISIBILITY_MODIFIER")
141+
.addMember("%S", "RedundantVisibilityModifier")
142+
.addMember("%S", "REDUNDANT_VISIBILITY_MODIFIER")
144143
.build()
145144
)
146145
file.addType(TypeSpec.objectBuilder("Res").also { resObject ->
@@ -202,7 +201,7 @@ internal fun getResFileSpec(
202201
//
203202
// if accessor initializers are extracted from the single object but located in the same file
204203
// then a build may fail with: org.jetbrains.org.objectweb.asm.ClassTooLargeException: Class too large: Res$drawable
205-
private const val ITEMS_PER_FILE_LIMIT = 500
204+
private const val ITEMS_PER_FILE_LIMIT = 100
206205
internal fun getAccessorsSpecs(
207206
//type -> id -> items
208207
resources: Map<ResourceType, Map<String, List<ResourceItem>>>,
@@ -248,20 +247,45 @@ private fun getChunkFileSpec(
248247
return FileSpec.builder(packageName, fileName).also { chunkFile ->
249248
chunkFile.addAnnotation(
250249
AnnotationSpec.builder(ClassName("kotlin", "OptIn"))
251-
.addMember("org.jetbrains.compose.resources.InternalResourceApi::class")
250+
.addMember("%T::class", internalAnnotationClass)
252251
.build()
253252
)
254253

255-
val objectSpec = TypeSpec.objectBuilder(chunkClassName).also { typeObject ->
256-
typeObject.addModifiers(KModifier.PRIVATE)
257-
val properties = idToResources.keys.map { resName ->
258-
PropertySpec.builder(resName, type.getClassName())
259-
.delegate("\nlazy·{ %N() }", "init_$resName")
260-
.build()
261-
}
262-
typeObject.addProperties(properties)
263-
}.build()
264-
chunkFile.addType(objectSpec)
254+
chunkFile.addProperty(
255+
PropertySpec.builder("MD", String::class)
256+
.addModifiers(KModifier.PRIVATE, KModifier.CONST)
257+
.initializer("%S", moduleDir)
258+
.build()
259+
)
260+
261+
idToResources.forEach { (resName, items) ->
262+
val initializer = CodeBlock.builder()
263+
.beginControlFlow("lazy {")
264+
.apply {
265+
if (type.requiresKeyName()) {
266+
add("%T(%S, %S, setOf(\n", type.getClassName(), "$type:$resName", resName)
267+
} else {
268+
add("%T(%S, setOf(\n", type.getClassName(), "$type:$resName")
269+
}
270+
items.forEach { item ->
271+
add(" %T(setOf(", resourceItemClass)
272+
addQualifiers(item)
273+
add("), ")
274+
//file separator should be '/' on all platforms
275+
add("\"${'$'}{MD}${item.path.invariantSeparatorsPathString}\", ${item.offset}, ${item.size}")
276+
add("),\n")
277+
}
278+
add("))\n")
279+
}
280+
.endControlFlow()
281+
.build()
282+
283+
val accessor = PropertySpec.builder(resName, type.getClassName(), resModifier)
284+
.receiver(ClassName(packageName, "Res", type.accessorName))
285+
.delegate(initializer)
286+
.build()
287+
chunkFile.addProperty(accessor)
288+
}
265289

266290
//__collect${chunkClassName}Resources function
267291
chunkFile.addFunction(
@@ -274,47 +298,11 @@ private fun getChunkFileSpec(
274298
)
275299
.also { collectFun ->
276300
idToResources.keys.forEach { resName ->
277-
collectFun.addStatement("map.put(%S, $chunkClassName.%N)", resName, resName)
301+
collectFun.addStatement("map.put(%S, %N.%N.%N)", resName, "Res", type.accessorName, resName)
278302
}
279303
}
280304
.build()
281305
)
282-
283-
idToResources.forEach { (resName, items) ->
284-
val accessor = PropertySpec.builder(resName, type.getClassName(), resModifier)
285-
.receiver(ClassName(packageName, "Res", type.accessorName))
286-
.getter(FunSpec.getterBuilder().addStatement("return $chunkClassName.%N", resName).build())
287-
.build()
288-
chunkFile.addProperty(accessor)
289-
290-
val initializer = FunSpec.builder("init_$resName")
291-
.addModifiers(KModifier.PRIVATE)
292-
.returns(type.getClassName())
293-
.addStatement(
294-
CodeBlock.builder()
295-
.add("return %T(\n", type.getClassName()).withIndent {
296-
add("%S,", "$type:$resName")
297-
if (type.requiresKeyName()) add(" %S,", resName)
298-
withIndent {
299-
add("\nsetOf(\n").withIndent {
300-
items.forEach { item ->
301-
add("%T(", resourceItemClass)
302-
add("setOf(").addQualifiers(item).add("), ")
303-
//file separator should be '/' on all platforms
304-
add("\"$moduleDir${item.path.invariantSeparatorsPathString}\", ")
305-
add("${item.offset}, ${item.size}")
306-
add("),\n")
307-
}
308-
}
309-
add(")\n")
310-
}
311-
}
312-
.add(")")
313-
.build().toString()
314-
)
315-
.build()
316-
chunkFile.addFunction(initializer)
317-
}
318306
}.build()
319307
}
320308

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,23 @@ class ResourcesTest : GradlePluginTestBase() {
567567
gradle("generateBuildConfig")
568568
}
569569

570+
//https://github.com/JetBrains/compose-multiplatform/issues/4194
571+
//https://github.com/JetBrains/compose-multiplatform/issues/4285
572+
//https://youtrack.jetbrains.com/issue/CMP-7934
573+
//
574+
// 25_000 icons + (25_000 * 80) strings!!!
575+
@Test
576+
fun testHugeNumberOfResources(): Unit = with(testProject("misc/hugeResources")) {
577+
gradle(":generateResourceFiles")
578+
gradle(":generateResourceAccessorsForCommonMain").checks {
579+
val buildPath = "build/generated/compose/resourceGenerator/kotlin/commonMainResourceAccessors/app/group/huge/generated/resources"
580+
assertEqualTextFiles(file("$buildPath/Drawable0.commonMain.kt"), file("expected/Drawable0.commonMain.kt"))
581+
assertEqualTextFiles(file("$buildPath/Drawable100.commonMain.kt"), file("expected/Drawable100.commonMain.kt"))
582+
assertEqualTextFiles(file("$buildPath/String0.commonMain.kt"), file("expected/String0.commonMain.kt"))
583+
assertEqualTextFiles(file("$buildPath/String100.commonMain.kt"), file("expected/String100.commonMain.kt"))
584+
}
585+
}
586+
570587
private fun assertDirectoriesContentEquals(actual: File, expected: File) {
571588
require(expected.isDirectory)
572589
require(actual.isDirectory)
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,23 @@
1-
@file:OptIn(org.jetbrains.compose.resources.InternalResourceApi::class)
1+
@file:OptIn(InternalResourceApi::class)
22

33
package my.lib.res
44

55
import kotlin.OptIn
66
import kotlin.String
77
import kotlin.collections.MutableMap
88
import org.jetbrains.compose.resources.InternalResourceApi
9+
import org.jetbrains.compose.resources.ResourceItem
910
import org.jetbrains.compose.resources.StringResource
1011

11-
private object AndroidMainString0 {
12-
public val android_str: StringResource by
13-
lazy { init_android_str() }
14-
}
12+
private const val MD: String = "composeResources/my.lib.res/"
13+
14+
public val Res.string.android_str: StringResource by lazy {
15+
StringResource("string:android_str", "android_str", setOf(
16+
ResourceItem(setOf(), "${MD}values/strings.androidMain.cvr", 10, 39),
17+
))
18+
}
1519

1620
@InternalResourceApi
1721
internal fun _collectAndroidMainString0Resources(map: MutableMap<String, StringResource>) {
18-
map.put("android_str", AndroidMainString0.android_str)
22+
map.put("android_str", Res.string.android_str)
1923
}
20-
21-
public val Res.string.android_str: StringResource
22-
get() = AndroidMainString0.android_str
23-
24-
private fun init_android_str(): StringResource = org.jetbrains.compose.resources.StringResource(
25-
"string:android_str", "android_str",
26-
setOf(
27-
org.jetbrains.compose.resources.ResourceItem(setOf(), "composeResources/my.lib.res/values/strings.androidMain.cvr", 10, 39),
28-
)
29-
)

gradle-plugins/compose/src/test/test-projects/misc/commonResources/expected-open-res/androidMainResourceCollectors/my/lib/res/ActualResourceCollectors.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,4 @@ public actual val Res.allFontResources: Map<String, FontResource> by lazy {
3939
val map = mutableMapOf<String, FontResource>()
4040
_collectCommonMainFont0Resources(map)
4141
return@lazy map
42-
}
42+
}
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
@file:OptIn(org.jetbrains.compose.resources.InternalResourceApi::class)
1+
@file:OptIn(InternalResourceApi::class)
22

33
package my.lib.res
44

@@ -7,83 +7,52 @@ import kotlin.String
77
import kotlin.collections.MutableMap
88
import org.jetbrains.compose.resources.DrawableResource
99
import org.jetbrains.compose.resources.InternalResourceApi
10-
11-
private object CommonMainDrawable0 {
12-
public val _3_strange_name: DrawableResource by
13-
lazy { init__3_strange_name() }
14-
15-
public val camelCaseName: DrawableResource by
16-
lazy { init_camelCaseName() }
17-
18-
public val `is`: DrawableResource by
19-
lazy { init_is() }
20-
21-
public val vector: DrawableResource by
22-
lazy { init_vector() }
23-
24-
public val vector_2: DrawableResource by
25-
lazy { init_vector_2() }
26-
}
10+
import org.jetbrains.compose.resources.LanguageQualifier
11+
import org.jetbrains.compose.resources.RegionQualifier
12+
import org.jetbrains.compose.resources.ResourceItem
13+
import org.jetbrains.compose.resources.ThemeQualifier
14+
15+
private const val MD: String = "composeResources/my.lib.res/"
16+
17+
public val Res.drawable._3_strange_name: DrawableResource by lazy {
18+
DrawableResource("drawable:_3_strange_name", setOf(
19+
ResourceItem(setOf(), "${MD}drawable/3-strange-name.xml", -1, -1),
20+
))
21+
}
22+
23+
public val Res.drawable.camelCaseName: DrawableResource by lazy {
24+
DrawableResource("drawable:camelCaseName", setOf(
25+
ResourceItem(setOf(), "${MD}drawable/camelCaseName.xml", -1, -1),
26+
))
27+
}
28+
29+
public val Res.drawable.`is`: DrawableResource by lazy {
30+
DrawableResource("drawable:is", setOf(
31+
ResourceItem(setOf(), "${MD}drawable/is.xml", -1, -1),
32+
))
33+
}
34+
35+
public val Res.drawable.vector: DrawableResource by lazy {
36+
DrawableResource("drawable:vector", setOf(
37+
ResourceItem(setOf(LanguageQualifier("ast"), ), "${MD}drawable-ast/vector.xml", -1, -1),
38+
ResourceItem(setOf(LanguageQualifier("au"), RegionQualifier("US"), ), "${MD}drawable-au-rUS/vector.xml", -1, -1),
39+
ResourceItem(setOf(ThemeQualifier.DARK, LanguageQualifier("ge"), ), "${MD}drawable-dark-ge/vector.xml", -1, -1),
40+
ResourceItem(setOf(LanguageQualifier("en"), ), "${MD}drawable-en/vector.xml", -1, -1),
41+
ResourceItem(setOf(), "${MD}drawable/vector.xml", -1, -1),
42+
))
43+
}
44+
45+
public val Res.drawable.vector_2: DrawableResource by lazy {
46+
DrawableResource("drawable:vector_2", setOf(
47+
ResourceItem(setOf(), "${MD}drawable/vector_2.xml", -1, -1),
48+
))
49+
}
2750

2851
@InternalResourceApi
2952
internal fun _collectCommonMainDrawable0Resources(map: MutableMap<String, DrawableResource>) {
30-
map.put("_3_strange_name", CommonMainDrawable0._3_strange_name)
31-
map.put("camelCaseName", CommonMainDrawable0.camelCaseName)
32-
map.put("is", CommonMainDrawable0.`is`)
33-
map.put("vector", CommonMainDrawable0.vector)
34-
map.put("vector_2", CommonMainDrawable0.vector_2)
53+
map.put("_3_strange_name", Res.drawable._3_strange_name)
54+
map.put("camelCaseName", Res.drawable.camelCaseName)
55+
map.put("is", Res.drawable.`is`)
56+
map.put("vector", Res.drawable.vector)
57+
map.put("vector_2", Res.drawable.vector_2)
3558
}
36-
37-
public val Res.drawable._3_strange_name: DrawableResource
38-
get() = CommonMainDrawable0._3_strange_name
39-
40-
private fun init__3_strange_name(): DrawableResource = org.jetbrains.compose.resources.DrawableResource(
41-
"drawable:_3_strange_name",
42-
setOf(
43-
org.jetbrains.compose.resources.ResourceItem(setOf(), "composeResources/my.lib.res/drawable/3-strange-name.xml", -1, -1),
44-
)
45-
)
46-
47-
public val Res.drawable.camelCaseName: DrawableResource
48-
get() = CommonMainDrawable0.camelCaseName
49-
50-
private fun init_camelCaseName(): DrawableResource = org.jetbrains.compose.resources.DrawableResource(
51-
"drawable:camelCaseName",
52-
setOf(
53-
org.jetbrains.compose.resources.ResourceItem(setOf(), "composeResources/my.lib.res/drawable/camelCaseName.xml", -1, -1),
54-
)
55-
)
56-
57-
public val Res.drawable.`is`: DrawableResource
58-
get() = CommonMainDrawable0.`is`
59-
60-
private fun init_is(): DrawableResource = org.jetbrains.compose.resources.DrawableResource(
61-
"drawable:is",
62-
setOf(
63-
org.jetbrains.compose.resources.ResourceItem(setOf(), "composeResources/my.lib.res/drawable/is.xml", -1, -1),
64-
)
65-
)
66-
67-
public val Res.drawable.vector: DrawableResource
68-
get() = CommonMainDrawable0.vector
69-
70-
private fun init_vector(): DrawableResource = org.jetbrains.compose.resources.DrawableResource(
71-
"drawable:vector",
72-
setOf(
73-
org.jetbrains.compose.resources.ResourceItem(setOf(org.jetbrains.compose.resources.LanguageQualifier("ast"), ), "composeResources/my.lib.res/drawable-ast/vector.xml", -1, -1),
74-
org.jetbrains.compose.resources.ResourceItem(setOf(org.jetbrains.compose.resources.LanguageQualifier("au"), org.jetbrains.compose.resources.RegionQualifier("US"), ), "composeResources/my.lib.res/drawable-au-rUS/vector.xml", -1, -1),
75-
org.jetbrains.compose.resources.ResourceItem(setOf(org.jetbrains.compose.resources.ThemeQualifier.DARK, org.jetbrains.compose.resources.LanguageQualifier("ge"), ), "composeResources/my.lib.res/drawable-dark-ge/vector.xml", -1, -1),
76-
org.jetbrains.compose.resources.ResourceItem(setOf(org.jetbrains.compose.resources.LanguageQualifier("en"), ), "composeResources/my.lib.res/drawable-en/vector.xml", -1, -1),
77-
org.jetbrains.compose.resources.ResourceItem(setOf(), "composeResources/my.lib.res/drawable/vector.xml", -1, -1),
78-
)
79-
)
80-
81-
public val Res.drawable.vector_2: DrawableResource
82-
get() = CommonMainDrawable0.vector_2
83-
84-
private fun init_vector_2(): DrawableResource = org.jetbrains.compose.resources.DrawableResource(
85-
"drawable:vector_2",
86-
setOf(
87-
org.jetbrains.compose.resources.ResourceItem(setOf(), "composeResources/my.lib.res/drawable/vector_2.xml", -1, -1),
88-
)
89-
)
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
@file:OptIn(org.jetbrains.compose.resources.InternalResourceApi::class)
1+
@file:OptIn(InternalResourceApi::class)
22

33
package my.lib.res
44

@@ -7,24 +7,19 @@ import kotlin.String
77
import kotlin.collections.MutableMap
88
import org.jetbrains.compose.resources.FontResource
99
import org.jetbrains.compose.resources.InternalResourceApi
10+
import org.jetbrains.compose.resources.LanguageQualifier
11+
import org.jetbrains.compose.resources.ResourceItem
1012

11-
private object CommonMainFont0 {
12-
public val emptyFont: FontResource by
13-
lazy { init_emptyFont() }
14-
}
13+
private const val MD: String = "composeResources/my.lib.res/"
14+
15+
public val Res.font.emptyFont: FontResource by lazy {
16+
FontResource("font:emptyFont", setOf(
17+
ResourceItem(setOf(LanguageQualifier("en"), ), "${MD}font-en/emptyFont.otf", -1, -1),
18+
ResourceItem(setOf(), "${MD}font/emptyFont.otf", -1, -1),
19+
))
20+
}
1521

1622
@InternalResourceApi
1723
internal fun _collectCommonMainFont0Resources(map: MutableMap<String, FontResource>) {
18-
map.put("emptyFont", CommonMainFont0.emptyFont)
24+
map.put("emptyFont", Res.font.emptyFont)
1925
}
20-
21-
public val Res.font.emptyFont: FontResource
22-
get() = CommonMainFont0.emptyFont
23-
24-
private fun init_emptyFont(): FontResource = org.jetbrains.compose.resources.FontResource(
25-
"font:emptyFont",
26-
setOf(
27-
org.jetbrains.compose.resources.ResourceItem(setOf(org.jetbrains.compose.resources.LanguageQualifier("en"), ), "composeResources/my.lib.res/font-en/emptyFont.otf", -1, -1),
28-
org.jetbrains.compose.resources.ResourceItem(setOf(), "composeResources/my.lib.res/font/emptyFont.otf", -1, -1),
29-
)
30-
)

0 commit comments

Comments
 (0)