@@ -116,22 +116,9 @@ internal abstract class GenerateImageVectorsTask : DefaultTask() {
116116 )
117117 }
118118
119- // Check for case-insensitive duplicates that would cause file overwrites on case-insensitive file systems
120- val caseInsensitiveDuplicates = iconNames
121- .groupBy { it.lowercase() }
122- .filter { it.value.size > 1 && it.value.distinct().size > 1 }
123- .values
124- .flatten()
125- .distinct()
119+ // Check for duplicates with nested pack awareness
120+ validateDuplicates(iconFiles.files.toList(), iconNames)
126121
127- if (caseInsensitiveDuplicates.isNotEmpty()) {
128- throw GradleException (
129- " Found icon names that would collide on case-insensitive file systems (macOS/Windows): " +
130- " ${caseInsensitiveDuplicates.joinToString(" , " )} . " +
131- " These icons would overwrite each other during generation. " +
132- " Please rename the source files to avoid case-insensitive duplicates." ,
133- )
134- }
135122
136123 if (iconPack.isPresent && iconPack.get().targetSourceSet.get() == sourceSet.get()) {
137124 generateIconPack(outputDirectory = outputDirectory)
@@ -408,4 +395,106 @@ internal abstract class GenerateImageVectorsTask : DefaultTask() {
408395
409396 return null
410397 }
398+
399+ private fun validateDuplicates (files : List <File >, iconNames : List <String >) {
400+ if (iconPack.isPresent) {
401+ val pack = iconPack.get()
402+ val nestedPacks = pack.nestedPacks.get()
403+ val useFlatPackage = pack.useFlatPackage.get()
404+
405+ if (nestedPacks.isNotEmpty()) {
406+ // Build map: file -> nested pack name
407+ val sourceFolderToNestedPack = nestedPacks.associateBy { it.sourceFolder.get() }
408+ val fileToNestedPack = files.associateWith { file ->
409+ sourceFolderToNestedPack[file.parentFile.name]?.name?.get()
410+ }
411+
412+ // Group by nested pack (or single group if useFlatPackage)
413+ val iconsByPack = files.groupBy { file ->
414+ if (useFlatPackage) {
415+ pack.name.get() // All in same pack when flat
416+ } else {
417+ val nestedPackName = fileToNestedPack[file]
418+ if (nestedPackName != null ) " ${pack.name.get()} .$nestedPackName " else pack.name.get()
419+ }
420+ }
421+
422+ iconsByPack.forEach { (packIdentifier, filesInPack) ->
423+ val names = filesInPack.map { IconNameFormatter .format(name = it.name) }
424+
425+ // Check exact duplicates within this pack/group
426+ val exactDuplicates = names
427+ .groupBy { it }
428+ .filter { it.value.size > 1 }
429+ .keys
430+ .toList()
431+
432+ if (exactDuplicates.isNotEmpty()) {
433+ throw GradleException (
434+ " Found duplicate icon names in \" $packIdentifier \" : ${exactDuplicates.joinToString(" , " )} . " +
435+ " Each icon must have a unique name. " +
436+ " Please rename the source files to avoid duplicates." ,
437+ )
438+ }
439+
440+ // Check case-insensitive duplicates within this pack/group
441+ val caseInsensitiveDuplicates = names
442+ .groupBy { it.lowercase() }
443+ .filter { it.value.size > 1 && it.value.distinct().size > 1 }
444+ .values
445+ .flatten()
446+ .distinct()
447+
448+ if (caseInsensitiveDuplicates.isNotEmpty()) {
449+ throw GradleException (
450+ " Found icon names that would collide on case-insensitive file systems (macOS/Windows) in \" $packIdentifier \" : " +
451+ " ${caseInsensitiveDuplicates.joinToString(" , " )} . " +
452+ " These icons would overwrite each other during generation. " +
453+ " Please rename the source files to avoid case-insensitive duplicates." ,
454+ )
455+ }
456+ }
457+ } else {
458+ // Single pack - check all files together
459+ checkDuplicatesInIconNames(iconNames, " icon pack \" ${pack.name.get()} \" " )
460+ }
461+ } else {
462+ // No icon pack - check all files together
463+ checkDuplicatesInIconNames(iconNames, " package \" ${packageName.get()} \" " )
464+ }
465+ }
466+
467+ private fun checkDuplicatesInIconNames (names : List <String >, context : String ) {
468+ // Check exact duplicates
469+ val exactDuplicates = names
470+ .groupBy { it }
471+ .filter { it.value.size > 1 }
472+ .keys
473+ .toList()
474+
475+ if (exactDuplicates.isNotEmpty()) {
476+ throw GradleException (
477+ " Found duplicate icon names in $context : ${exactDuplicates.joinToString(" , " )} . " +
478+ " Each icon must have a unique name. " +
479+ " Please rename the source files to avoid duplicates." ,
480+ )
481+ }
482+
483+ // Check case-insensitive duplicates
484+ val caseInsensitiveDuplicates = names
485+ .groupBy { it.lowercase() }
486+ .filter { it.value.size > 1 && it.value.distinct().size > 1 }
487+ .values
488+ .flatten()
489+ .distinct()
490+
491+ if (caseInsensitiveDuplicates.isNotEmpty()) {
492+ throw GradleException (
493+ " Found icon names that would collide on case-insensitive file systems (macOS/Windows) in $context : " +
494+ " ${caseInsensitiveDuplicates.joinToString(" , " )} . " +
495+ " These icons would overwrite each other during generation. " +
496+ " Please rename the source files to avoid case-insensitive duplicates." ,
497+ )
498+ }
499+ }
411500}
0 commit comments