From be794bd1d5754957ced08d072fc264e26c196c6c Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 15 May 2026 17:54:12 +0800 Subject: [PATCH 1/6] Check DuplicatesStrategy for merging transformers --- docs/changes/README.md | 2 ++ .../ServiceFileTransformerTest.kt | 11 ++++++- .../internal/DuplicatesStrategyChecker.kt | 29 +++++++++++++++++++ .../transformers/AppendingTransformer.kt | 5 +++- .../DeduplicatingResourceTransformer.kt | 7 +++-- .../PatternFilterableResourceTransformer.kt | 3 +- .../PreserveFirstFoundResourceTransformer.kt | 5 +++- .../transformers/PropertiesFileTransformer.kt | 3 +- .../transformers/XmlAppendingTransformer.kt | 5 +++- 9 files changed, 62 insertions(+), 8 deletions(-) create mode 100644 src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DuplicatesStrategyChecker.kt diff --git a/docs/changes/README.md b/docs/changes/README.md index e2da68391..ebc352beb 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -5,6 +5,8 @@ ### Added +- Check `DuplicatesStrategy` for merging transformers. ([#2026](https://github.com/GradleUp/shadow/pull/2026)) + This will log warnings if the wrong `DuplicatesStrategy`s are applied in the Gradle configurations for built-in `ResourceTransformer`s. - Expose `patternSet` of `ComponentsXmlResourceTransformer` as `public`. ([#2028](https://github.com/GradleUp/shadow/pull/2028)) - Expose `patternSet` of `GroovyExtensionModuleTransformer` as `public`. ([#2028](https://github.com/GradleUp/shadow/pull/2028)) - Expose `patternSet` of `Log4j2PluginsCacheFileTransformer` as `public`. ([#2028](https://github.com/GradleUp/shadow/pull/2028)) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt index f1902d590..5b2972d87 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -1,6 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import assertk.assertThat +import assertk.assertions.contains import assertk.assertions.containsMatch import assertk.assertions.isEqualTo import com.github.jengelman.gradle.plugins.shadow.testkit.getContent @@ -221,7 +222,15 @@ class ServiceFileTransformerTest : BaseTransformerTest() { ) { writeDuplicatesStrategy(strategy) - runWithSuccess(shadowJarPath) + val result = runWithSuccess(shadowJarPath) + + if (strategy == EXCLUDE) { + assertThat(result.output) + .contains( + "META-INF/services/com.acme.Foo' is matched by com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer (a merging transformer) but its DuplicatesStrategy is EXCLUDE — duplicates may be silently dropped before merging.", + "META-INF/services/org.apache.maven.Shade' is matched by com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer (a merging transformer) but its DuplicatesStrategy is EXCLUDE — duplicates may be silently dropped before merging.", + ) + } assertThat(outputShadowedJar).useAll { getContent(ENTRY_SERVICES_SHADE).isEqualTo(firstValue) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DuplicatesStrategyChecker.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DuplicatesStrategyChecker.kt new file mode 100644 index 000000000..cad251195 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DuplicatesStrategyChecker.kt @@ -0,0 +1,29 @@ +package com.github.jengelman.gradle.plugins.shadow.internal + +import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer +import org.gradle.api.file.DuplicatesStrategy +import org.gradle.api.file.FileCopyDetails +import org.gradle.api.file.FileTreeElement +import org.gradle.api.logging.Logging + +internal fun ResourceTransformer.checkDupStrategy( + canTransformResource: Boolean, + element: FileTreeElement, +) { + when { + !canTransformResource -> return + element !is FileCopyDetails -> return + element.duplicatesStrategy == DuplicatesStrategy.EXCLUDE -> { + val logger = Logging.getLogger(this::class.java) + logger.warn( + """ + ${element.path}' is matched by ${this::class.qualifiedName} (a merging transformer) but its DuplicatesStrategy is ${element.duplicatesStrategy} — duplicates may be silently dropped before merging. + Set it to INCLUDE or WARN to ensure all duplicates are processed by the transformer. + See https://gradleup.com/shadow/configuration/merging/#handling-duplicates-strategy for more details. + """ + .trimIndent() + ) + } + else -> Unit + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt index 7e29c4ceb..ce7b080b3 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt @@ -1,5 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow.transformers +import com.github.jengelman.gradle.plugins.shadow.internal.checkDupStrategy import com.github.jengelman.gradle.plugins.shadow.internal.property import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry import java.io.ByteArrayOutputStream @@ -32,7 +33,9 @@ constructor(final override val objectFactory: ObjectFactory) : ResourceTransform @get:Input public open val separator: Property = objectFactory.property(DEFAULT_SEPARATOR) override fun canTransformResource(element: FileTreeElement): Boolean { - return resource.get().equals(element.path, ignoreCase = true) + return resource.get().equals(element.path, ignoreCase = true).also { flag -> + checkDupStrategy(flag, element) + } } override fun transform(context: TransformerContext) { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DeduplicatingResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DeduplicatingResourceTransformer.kt index 61d95e66d..c2cc23985 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DeduplicatingResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DeduplicatingResourceTransformer.kt @@ -1,5 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow.transformers +import com.github.jengelman.gradle.plugins.shadow.internal.checkDupStrategy import com.github.jengelman.gradle.plugins.shadow.tasks.FindResourceInClasspath import java.io.File import javax.inject.Inject @@ -66,8 +67,10 @@ public open class DeduplicatingResourceTransformer( val file = element.file val hash = file.sha256Hex() - val pathInfos = - sources.computeIfAbsent(element.path) { PathInfos(patternSpec.isSatisfiedBy(element)) } + val flag = patternSpec.isSatisfiedBy(element) + checkDupStrategy(flag, element) + + val pathInfos = sources.computeIfAbsent(element.path) { PathInfos(flag) } val retainInOutput = pathInfos.addFile(hash, file) return !retainInOutput diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer.kt index 5c943d62e..932d95344 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer.kt @@ -1,5 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow.transformers +import com.github.jengelman.gradle.plugins.shadow.internal.checkDupStrategy import com.github.jengelman.gradle.plugins.shadow.internal.unsafeLazy import org.gradle.api.file.FileTreeElement import org.gradle.api.specs.Spec @@ -24,7 +25,7 @@ public abstract class PatternFilterableResourceTransformer( } override fun canTransformResource(element: FileTreeElement): Boolean { - return patternSpec.isSatisfiedBy(element) + return patternSpec.isSatisfiedBy(element).also { flag -> checkDupStrategy(flag, element) } } @Input // Trigger task executions after includes changed. diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PreserveFirstFoundResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PreserveFirstFoundResourceTransformer.kt index 915f2f360..5c86eefc4 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PreserveFirstFoundResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PreserveFirstFoundResourceTransformer.kt @@ -1,5 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow.transformers +import com.github.jengelman.gradle.plugins.shadow.internal.checkDupStrategy import com.github.jengelman.gradle.plugins.shadow.internal.setProperty import com.github.jengelman.gradle.plugins.shadow.internal.unsafeLazy import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar @@ -48,6 +49,8 @@ public open class PreserveFirstFoundResourceTransformer( override fun canTransformResource(element: FileTreeElement): Boolean { // Init once before patternSpec is accessed. includeResources - return patternSpec.isSatisfiedBy(element) && !found.add(element.path) + val flag = patternSpec.isSatisfiedBy(element) + checkDupStrategy(flag, element) + return flag && !found.add(element.path) } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt index 7ce7e950a..20e13f7d4 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt @@ -1,6 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.internal.ReproducibleProperties +import com.github.jengelman.gradle.plugins.shadow.internal.checkDupStrategy import com.github.jengelman.gradle.plugins.shadow.internal.mapProperty import com.github.jengelman.gradle.plugins.shadow.internal.property import com.github.jengelman.gradle.plugins.shadow.internal.setProperty @@ -137,7 +138,7 @@ constructor(final override val objectFactory: ObjectFactory) : ResourceTransform path in paths -> true paths.any { it.toRegex().containsMatchIn(path) } -> true else -> mappings.isEmpty() && paths.isEmpty() && path.endsWith(PROPERTIES_SUFFIX) - } + }.also { checkDupStrategy(it, element) } } override fun transform(context: TransformerContext) { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt index 990bb90f2..be30afba0 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt @@ -1,5 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow.transformers +import com.github.jengelman.gradle.plugins.shadow.internal.checkDupStrategy import com.github.jengelman.gradle.plugins.shadow.internal.property import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry import java.io.IOException @@ -38,7 +39,9 @@ constructor(final override val objectFactory: ObjectFactory) : ResourceTransform @get:Input public open val resource: Property = objectFactory.property("") override fun canTransformResource(element: FileTreeElement): Boolean { - return resource.get().equals(element.path, ignoreCase = true) + return resource.get().equals(element.path, ignoreCase = true).also { flag -> + checkDupStrategy(flag, element) + } } override fun transform(context: TransformerContext) { From f638ce48ce95feeee921f4aef7e90170c4fcfd14 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 23 Jun 2026 13:15:43 +0800 Subject: [PATCH 2/6] Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- docs/changes/README.md | 2 +- .../plugins/shadow/transformers/ServiceFileTransformerTest.kt | 4 ++-- .../plugins/shadow/internal/DuplicatesStrategyChecker.kt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index ebc352beb..7f9d6d88e 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -6,7 +6,7 @@ ### Added - Check `DuplicatesStrategy` for merging transformers. ([#2026](https://github.com/GradleUp/shadow/pull/2026)) - This will log warnings if the wrong `DuplicatesStrategy`s are applied in the Gradle configurations for built-in `ResourceTransformer`s. + This will log warnings if the wrong `DuplicatesStrategy` values are applied in the Gradle configurations for built-in `ResourceTransformer`s. - Expose `patternSet` of `ComponentsXmlResourceTransformer` as `public`. ([#2028](https://github.com/GradleUp/shadow/pull/2028)) - Expose `patternSet` of `GroovyExtensionModuleTransformer` as `public`. ([#2028](https://github.com/GradleUp/shadow/pull/2028)) - Expose `patternSet` of `Log4j2PluginsCacheFileTransformer` as `public`. ([#2028](https://github.com/GradleUp/shadow/pull/2028)) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt index 5b2972d87..81be856ea 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -227,8 +227,8 @@ class ServiceFileTransformerTest : BaseTransformerTest() { if (strategy == EXCLUDE) { assertThat(result.output) .contains( - "META-INF/services/com.acme.Foo' is matched by com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer (a merging transformer) but its DuplicatesStrategy is EXCLUDE — duplicates may be silently dropped before merging.", - "META-INF/services/org.apache.maven.Shade' is matched by com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer (a merging transformer) but its DuplicatesStrategy is EXCLUDE — duplicates may be silently dropped before merging.", + "'META-INF/services/com.acme.Foo' is matched by com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer but its DuplicatesStrategy is EXCLUDE — duplicates may be silently dropped before the transformer processes them.", + "'META-INF/services/org.apache.maven.Shade' is matched by com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer but its DuplicatesStrategy is EXCLUDE — duplicates may be silently dropped before the transformer processes them.", ) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DuplicatesStrategyChecker.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DuplicatesStrategyChecker.kt index cad251195..8cfda4809 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DuplicatesStrategyChecker.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DuplicatesStrategyChecker.kt @@ -17,7 +17,7 @@ internal fun ResourceTransformer.checkDupStrategy( val logger = Logging.getLogger(this::class.java) logger.warn( """ - ${element.path}' is matched by ${this::class.qualifiedName} (a merging transformer) but its DuplicatesStrategy is ${element.duplicatesStrategy} — duplicates may be silently dropped before merging. + '${element.path}' is matched by ${this::class.qualifiedName} but its DuplicatesStrategy is ${element.duplicatesStrategy} — duplicates may be silently dropped before the transformer processes them. Set it to INCLUDE or WARN to ensure all duplicates are processed by the transformer. See https://gradleup.com/shadow/configuration/merging/#handling-duplicates-strategy for more details. """ From 23627b70408a8485f8d09cc2d37c9b3090c64513 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 28 Jun 2026 14:51:22 +0800 Subject: [PATCH 3/6] Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- docs/changes/README.md | 2 +- .../plugins/shadow/internal/DuplicatesStrategyChecker.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 3f33a55a7..6affe8a1e 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -6,7 +6,7 @@ ### Added - Check `DuplicatesStrategy` for merging transformers. ([#2026](https://github.com/GradleUp/shadow/pull/2026)) - This will log warnings if the wrong `DuplicatesStrategy` values are applied in the Gradle configurations for built-in `ResourceTransformer`s. + This will log warnings when an incompatible `DuplicatesStrategy` (e.g., `EXCLUDE`) is applied in Gradle configuration for built-in `ResourceTransformer`s. - Expose `patternSet` of `ComponentsXmlResourceTransformer` as `public`. ([#2028](https://github.com/GradleUp/shadow/pull/2028)) - Expose `patternSet` of `GroovyExtensionModuleTransformer` as `public`. ([#2028](https://github.com/GradleUp/shadow/pull/2028)) - Expose `patternSet` of `Log4j2PluginsCacheFileTransformer` as `public`. ([#2028](https://github.com/GradleUp/shadow/pull/2028)) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DuplicatesStrategyChecker.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DuplicatesStrategyChecker.kt index 8cfda4809..1935e121c 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DuplicatesStrategyChecker.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DuplicatesStrategyChecker.kt @@ -7,11 +7,11 @@ import org.gradle.api.file.FileTreeElement import org.gradle.api.logging.Logging internal fun ResourceTransformer.checkDupStrategy( - canTransformResource: Boolean, + matched: Boolean, element: FileTreeElement, ) { when { - !canTransformResource -> return + !matched -> return element !is FileCopyDetails -> return element.duplicatesStrategy == DuplicatesStrategy.EXCLUDE -> { val logger = Logging.getLogger(this::class.java) From 147bccd3727fdcafd14f7c27398f658d7513c55a Mon Sep 17 00:00:00 2001 From: Goooler Date: Sun, 28 Jun 2026 15:52:20 +0800 Subject: [PATCH 4/6] Call checkDupStrategy in each transformer --- api/shadow.api | 9 +++++++++ .../transformers/ApacheLicenseResourceTransformer.kt | 6 ++++++ .../transformers/ApacheNoticeResourceTransformer.kt | 6 ++++++ .../transformers/ComponentsXmlResourceTransformer.kt | 6 ++++++ .../transformers/GroovyExtensionModuleTransformer.kt | 6 ++++++ .../transformers/Log4j2PluginsCacheFileTransformer.kt | 6 ++++++ .../shadow/transformers/ManifestAppenderTransformer.kt | 6 ++++++ .../shadow/transformers/ManifestResourceTransformer.kt | 6 ++++++ .../transformers/MergeLicenseResourceTransformer.kt | 6 ++++++ .../transformers/PatternFilterableResourceTransformer.kt | 3 +-- .../shadow/transformers/ServiceFileTransformer.kt | 6 ++++++ 11 files changed, 64 insertions(+), 2 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index 3363a12af..49a01ee0c 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -276,11 +276,13 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicen public fun ()V public fun (Lorg/gradle/api/tasks/util/PatternSet;)V public synthetic fun (Lorg/gradle/api/tasks/util/PatternSet;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z } public class com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer { public fun (Lorg/gradle/api/model/ObjectFactory;)V public fun (Lorg/gradle/api/model/ObjectFactory;Lorg/gradle/api/tasks/util/PatternSet;)V + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z public fun getAddHeader ()Lorg/gradle/api/provider/Property; public fun getCharsetName ()Lorg/gradle/api/provider/Property; public fun getCopyright ()Lorg/gradle/api/provider/Property; @@ -323,6 +325,7 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsX public fun ()V public fun (Lorg/gradle/api/tasks/util/PatternSet;)V public synthetic fun (Lorg/gradle/api/tasks/util/PatternSet;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z public fun hasTransformedResource ()Z public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V @@ -364,6 +367,7 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExten public fun ()V public fun (Lorg/gradle/api/tasks/util/PatternSet;)V public synthetic fun (Lorg/gradle/api/tasks/util/PatternSet;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z public fun hasTransformedResource ()Z public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V @@ -388,6 +392,7 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2Plugi public fun ()V public fun (Lorg/gradle/api/tasks/util/PatternSet;)V public synthetic fun (Lorg/gradle/api/tasks/util/PatternSet;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z public fun hasTransformedResource ()Z public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V @@ -397,6 +402,7 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/ManifestApp public fun (Lorg/gradle/api/model/ObjectFactory;)V public fun (Lorg/gradle/api/model/ObjectFactory;Lorg/gradle/api/tasks/util/PatternSet;)V public fun append (Ljava/lang/String;Ljava/lang/Comparable;)V + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z public fun getAttributes ()Lorg/gradle/api/provider/SetProperty; public final fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; public fun hasTransformedResource ()Z @@ -408,6 +414,7 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/ManifestRes public fun (Lorg/gradle/api/model/ObjectFactory;)V public fun (Lorg/gradle/api/model/ObjectFactory;Lorg/gradle/api/tasks/util/PatternSet;)V public fun attributes (Ljava/util/Map;)V + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z public fun getMainClass ()Lorg/gradle/api/provider/Property; public fun getManifestEntries ()Lorg/gradle/api/provider/MapProperty; public final fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; @@ -419,6 +426,7 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/ManifestRes public class com/github/jengelman/gradle/plugins/shadow/transformers/MergeLicenseResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer { public fun (Lorg/gradle/api/model/ObjectFactory;)V public fun (Lorg/gradle/api/model/ObjectFactory;Lorg/gradle/api/tasks/util/PatternSet;)V + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z public fun getArtifactLicense ()Lorg/gradle/api/file/RegularFileProperty; public fun getArtifactLicenseSpdxId ()Lorg/gradle/api/provider/Property; public fun getFirstSeparator ()Lorg/gradle/api/provider/Property; @@ -518,6 +526,7 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFile public fun ()V public fun (Lorg/gradle/api/tasks/util/PatternSet;)V public synthetic fun (Lorg/gradle/api/tasks/util/PatternSet;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z public fun getPath ()Ljava/lang/String; public fun hasTransformedResource ()Z public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt index 3a9826114..9589aaf56 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt @@ -1,5 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow.transformers +import com.github.jengelman.gradle.plugins.shadow.internal.checkDupStrategy +import org.gradle.api.file.FileTreeElement import org.gradle.api.tasks.util.PatternSet /** @@ -19,6 +21,10 @@ constructor( .apply { isCaseSensitive = false } .include(LICENSE_PATH, LICENSE_TXT_PATH, LICENSE_MD_PATH) ) : PatternFilterableResourceTransformer(patternSet = patternSet) { + override fun canTransformResource(element: FileTreeElement): Boolean { + return super.canTransformResource(element).also { flag -> checkDupStrategy(flag, element) } + } + private companion object { private const val LICENSE_PATH = "META-INF/LICENSE" private const val LICENSE_TXT_PATH = "META-INF/LICENSE.txt" diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt index 2276eff33..49a149037 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt @@ -1,5 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow.transformers +import com.github.jengelman.gradle.plugins.shadow.internal.checkDupStrategy import com.github.jengelman.gradle.plugins.shadow.internal.property import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry import java.nio.charset.Charset @@ -9,6 +10,7 @@ import java.util.Locale import java.util.TreeSet import javax.inject.Inject import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.file.FileTreeElement import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.Property import org.gradle.api.tasks.Input @@ -94,6 +96,10 @@ public open class ApacheNoticeResourceTransformer( .include(NOTICE_PATH, NOTICE_TXT_PATH, NOTICE_MD_PATH), ) + override fun canTransformResource(element: FileTreeElement): Boolean { + return super.canTransformResource(element).also { flag -> checkDupStrategy(flag, element) } + } + override fun transform(context: TransformerContext) { val projectName = projectName.get() val addHeader = addHeader.get() diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt index 7e0bd204c..29ccd667e 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt @@ -1,5 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow.transformers +import com.github.jengelman.gradle.plugins.shadow.internal.checkDupStrategy import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry import com.github.jengelman.gradle.plugins.shadow.relocation.relocateClass import java.io.BufferedInputStream @@ -11,6 +12,7 @@ import org.codehaus.plexus.util.xml.XmlStreamWriter import org.codehaus.plexus.util.xml.Xpp3Dom import org.codehaus.plexus.util.xml.Xpp3DomBuilder import org.codehaus.plexus.util.xml.Xpp3DomWriter +import org.gradle.api.file.FileTreeElement import org.gradle.api.tasks.Internal import org.gradle.api.tasks.util.PatternSet @@ -45,6 +47,10 @@ constructor(patternSet: PatternSet = PatternSet().include(COMPONENTS_XML_PATH)) return os.toByteArray() } + override fun canTransformResource(element: FileTreeElement): Boolean { + return super.canTransformResource(element).also { flag -> checkDupStrategy(flag, element) } + } + override fun transform(context: TransformerContext) { val newDom = try { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt index e75ccc031..ccac8374a 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt @@ -1,10 +1,12 @@ package com.github.jengelman.gradle.plugins.shadow.transformers +import com.github.jengelman.gradle.plugins.shadow.internal.checkDupStrategy import com.github.jengelman.gradle.plugins.shadow.internal.inputStream import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry import com.github.jengelman.gradle.plugins.shadow.relocation.relocateClass import java.util.Properties import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.file.FileTreeElement import org.gradle.api.tasks.util.PatternSet /** @@ -39,6 +41,10 @@ constructor( ) : PatternFilterableResourceTransformer(patternSet) { private val module = Properties() + override fun canTransformResource(element: FileTreeElement): Boolean { + return super.canTransformResource(element).also { flag -> checkDupStrategy(flag, element) } + } + override fun transform(context: TransformerContext) { val props = Properties() props.load(context.inputStream) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt index ed3c6e34f..9909bf7c3 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt @@ -1,5 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow.transformers +import com.github.jengelman.gradle.plugins.shadow.internal.checkDupStrategy import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import com.github.jengelman.gradle.plugins.shadow.relocation.relocateClass @@ -14,6 +15,7 @@ import org.apache.commons.io.output.CloseShieldOutputStream import org.apache.logging.log4j.core.config.plugins.processor.PluginCache import org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor.PLUGIN_CACHE_FILE import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.file.FileTreeElement import org.gradle.api.tasks.util.PatternSet /** @@ -34,6 +36,10 @@ constructor(patternSet: PatternSet = PatternSet().include(PLUGIN_CACHE_FILE)) : /** [Relocator] instances to share across the transformation stages. */ private val tempRelocators = mutableListOf() + override fun canTransformResource(element: FileTreeElement): Boolean { + return super.canTransformResource(element).also { flag -> checkDupStrategy(flag, element) } + } + override fun transform(context: TransformerContext) { val temporaryFile = createTempFile("Log4j2Plugins", ".dat") tempFiles.add(temporaryFile) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt index ca8eabdcc..3042ffa29 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt @@ -1,5 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow.transformers +import com.github.jengelman.gradle.plugins.shadow.internal.checkDupStrategy import com.github.jengelman.gradle.plugins.shadow.internal.setProperty import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry import java.io.ByteArrayOutputStream @@ -7,6 +8,7 @@ import java.io.IOException import java.util.jar.JarFile.MANIFEST_NAME import javax.inject.Inject import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.file.FileTreeElement import org.gradle.api.logging.Logging import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.SetProperty @@ -40,6 +42,10 @@ public open class ManifestAppenderTransformer( patternSet = PatternSet().apply { isCaseSensitive = false }.include(MANIFEST_NAME), ) + override fun canTransformResource(element: FileTreeElement): Boolean { + return super.canTransformResource(element).also { flag -> checkDupStrategy(flag, element) } + } + override fun transform(context: TransformerContext) { if (manifestContents.isEmpty()) { try { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt index ea187f370..a833c350f 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt @@ -1,5 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow.transformers +import com.github.jengelman.gradle.plugins.shadow.internal.checkDupStrategy import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey import com.github.jengelman.gradle.plugins.shadow.internal.mapProperty import com.github.jengelman.gradle.plugins.shadow.internal.property @@ -10,6 +11,7 @@ import java.util.jar.JarFile import java.util.jar.Manifest import javax.inject.Inject import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.file.FileTreeElement import org.gradle.api.logging.Logging import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.MapProperty @@ -49,6 +51,10 @@ public open class ManifestResourceTransformer( patternSet = PatternSet().apply { isCaseSensitive = false }.include(JarFile.MANIFEST_NAME), ) + override fun canTransformResource(element: FileTreeElement): Boolean { + return super.canTransformResource(element).also { flag -> checkDupStrategy(flag, element) } + } + override fun transform(context: TransformerContext) { // We just want to take the first manifest we come across as that's our project's manifest. This // is the behavior diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/MergeLicenseResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/MergeLicenseResourceTransformer.kt index 7abd3af79..11fd65a1d 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/MergeLicenseResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/MergeLicenseResourceTransformer.kt @@ -1,11 +1,13 @@ package com.github.jengelman.gradle.plugins.shadow.transformers +import com.github.jengelman.gradle.plugins.shadow.internal.checkDupStrategy import com.github.jengelman.gradle.plugins.shadow.internal.property import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry import java.nio.charset.StandardCharsets.UTF_8 import java.util.LinkedHashSet import javax.inject.Inject import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.file.FileTreeElement import org.gradle.api.file.RegularFileProperty import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.Property @@ -100,6 +102,10 @@ public open class MergeLicenseResourceTransformer( }, ) + override fun canTransformResource(element: FileTreeElement): Boolean { + return super.canTransformResource(element).also { flag -> checkDupStrategy(flag, element) } + } + override fun transform(context: TransformerContext) { transformInternal(context.inputStream.readAllBytes()) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer.kt index 932d95344..5c943d62e 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer.kt @@ -1,6 +1,5 @@ package com.github.jengelman.gradle.plugins.shadow.transformers -import com.github.jengelman.gradle.plugins.shadow.internal.checkDupStrategy import com.github.jengelman.gradle.plugins.shadow.internal.unsafeLazy import org.gradle.api.file.FileTreeElement import org.gradle.api.specs.Spec @@ -25,7 +24,7 @@ public abstract class PatternFilterableResourceTransformer( } override fun canTransformResource(element: FileTreeElement): Boolean { - return patternSpec.isSatisfiedBy(element).also { flag -> checkDupStrategy(flag, element) } + return patternSpec.isSatisfiedBy(element) } @Input // Trigger task executions after includes changed. diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt index bcb95ab21..71b6b58fe 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt @@ -1,9 +1,11 @@ package com.github.jengelman.gradle.plugins.shadow.transformers +import com.github.jengelman.gradle.plugins.shadow.internal.checkDupStrategy import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry import com.github.jengelman.gradle.plugins.shadow.relocation.relocateClass import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.file.FileTreeElement import org.gradle.api.tasks.Internal import org.gradle.api.tasks.util.PatternSet @@ -39,6 +41,10 @@ constructor( setIncludes(listOf("$value/**")) } + override fun canTransformResource(element: FileTreeElement): Boolean { + return super.canTransformResource(element).also { flag -> checkDupStrategy(flag, element) } + } + override fun transform(context: TransformerContext) { val resource = path + "/" + context.relocators.relocateClass(context.path.substringAfter("$path/")) From 74de036d21651a597f7121796e2984dbce4c4c35 Mon Sep 17 00:00:00 2001 From: Goooler Date: Sun, 28 Jun 2026 17:01:23 +0800 Subject: [PATCH 5/6] Test checkDupStrategyInvocationCount --- .../internal/DuplicatesStrategyChecker.kt | 4 + .../internal/DuplicatesStrategyCheckerTest.kt | 106 ++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DuplicatesStrategyCheckerTest.kt diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DuplicatesStrategyChecker.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DuplicatesStrategyChecker.kt index 1935e121c..021545c4f 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DuplicatesStrategyChecker.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DuplicatesStrategyChecker.kt @@ -5,11 +5,15 @@ import org.gradle.api.file.DuplicatesStrategy import org.gradle.api.file.FileCopyDetails import org.gradle.api.file.FileTreeElement import org.gradle.api.logging.Logging +import org.jetbrains.annotations.VisibleForTesting + +@VisibleForTesting internal var onCheckDupStrategyInvoked: (() -> Unit)? = null internal fun ResourceTransformer.checkDupStrategy( matched: Boolean, element: FileTreeElement, ) { + onCheckDupStrategyInvoked?.invoke() when { !matched -> return element !is FileCopyDetails -> return diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DuplicatesStrategyCheckerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DuplicatesStrategyCheckerTest.kt new file mode 100644 index 000000000..5b9b69215 --- /dev/null +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DuplicatesStrategyCheckerTest.kt @@ -0,0 +1,106 @@ +package com.github.jengelman.gradle.plugins.shadow.internal + +import assertk.assertThat +import assertk.assertions.isEqualTo +import com.github.jengelman.gradle.plugins.shadow.transformers.BaseTransformerTest.Companion.canTransformResource +import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer +import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer.Companion.create +import com.github.jengelman.gradle.plugins.shadow.util.testObjectFactory +import java.io.File +import java.lang.reflect.Modifier +import java.net.JarURLConnection +import java.nio.file.Path +import kotlin.io.path.createTempFile +import kotlin.io.path.exists +import kotlin.io.path.extension +import kotlin.io.path.isRegularFile +import kotlin.io.path.pathString +import kotlin.io.path.toPath +import kotlin.io.path.walk +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.io.TempDir + +class DuplicatesStrategyCheckerTest { + @TempDir lateinit var tempDir: Path + + @Test + fun checkDupStrategyInvocationCount() { + val allResourceTransformers = + getTransformerClasses().map { + it.create(testObjectFactory) + } + assertThat(allResourceTransformers.size).isEqualTo(17) + + var invocationCount = 0 + onCheckDupStrategyInvoked = { + invocationCount++ + } + try { + allResourceTransformers.forEach { + val file = createTempFile(directory = tempDir).toFile() + it.canTransformResource(path = file.path, file = file) + } + assertThat(invocationCount).isEqualTo(14) + } finally { + onCheckDupStrategyInvoked = null + } + } +} + +private fun getTransformerClasses(): List> { + val packageName = "com.github.jengelman.gradle.plugins.shadow.transformers" + val packagePath = packageName.replace('.', '/') + val classLoader = + Thread.currentThread().contextClassLoader ?: ResourceTransformer::class.java.classLoader + val resources = classLoader.getResources(packagePath) + val classes = mutableListOf>() + for (url in resources) { + when (url.protocol) { + "file" -> { + val directory = url.toURI().toPath() + if (directory.exists()) { + directory + .walk() + .filter { it.isRegularFile() && it.extension == "class" } + .forEach { file -> + val relativePath = file.pathString.substringAfter(packagePath).removeSuffix(".class") + val className = packageName + relativePath.replace(File.separatorChar, '.') + try { + val clazz = Class.forName(className) + if ( + ResourceTransformer::class.java.isAssignableFrom(clazz) && + !clazz.isInterface && + !Modifier.isAbstract(clazz.modifiers) + ) { + @Suppress("UNCHECKED_CAST") classes.add(clazz as Class) + } + } catch (_: Exception) {} + } + } + } + "jar" -> { + val connection = url.openConnection() as JarURLConnection + val jarFile = connection.jarFile + val entries = jarFile.entries() + while (entries.hasMoreElements()) { + val entry = entries.nextElement() + val name = entry.name + if (name.startsWith(packagePath) && name.endsWith(".class")) { + val className = name.removeSuffix(".class").replace('/', '.') + try { + val clazz = Class.forName(className) + if ( + ResourceTransformer::class.java.isAssignableFrom(clazz) && + !clazz.isInterface && + !Modifier.isAbstract(clazz.modifiers) + ) { + @Suppress("UNCHECKED_CAST") classes.add(clazz as Class) + } + } catch (_: Exception) {} + } + } + } + } + } + return classes +} From bc671ea9ecf3ea896c717c401ef9011af2dd0413 Mon Sep 17 00:00:00 2001 From: Goooler Date: Sun, 28 Jun 2026 17:36:23 +0800 Subject: [PATCH 6/6] Cleanups --- .../internal/DuplicatesStrategyCheckerTest.kt | 47 +++++++------------ 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DuplicatesStrategyCheckerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DuplicatesStrategyCheckerTest.kt index 5b9b69215..6696d3628 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DuplicatesStrategyCheckerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DuplicatesStrategyCheckerTest.kt @@ -7,7 +7,6 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransform import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer.Companion.create import com.github.jengelman.gradle.plugins.shadow.util.testObjectFactory import java.io.File -import java.lang.reflect.Modifier import java.net.JarURLConnection import java.nio.file.Path import kotlin.io.path.createTempFile @@ -17,6 +16,7 @@ import kotlin.io.path.isRegularFile import kotlin.io.path.pathString import kotlin.io.path.toPath import kotlin.io.path.walk +import kotlin.reflect.full.isSubclassOf import org.junit.jupiter.api.Test import org.junit.jupiter.api.io.TempDir @@ -32,9 +32,7 @@ class DuplicatesStrategyCheckerTest { assertThat(allResourceTransformers.size).isEqualTo(17) var invocationCount = 0 - onCheckDupStrategyInvoked = { - invocationCount++ - } + onCheckDupStrategyInvoked = { invocationCount++ } try { allResourceTransformers.forEach { val file = createTempFile(directory = tempDir).toFile() @@ -49,11 +47,22 @@ class DuplicatesStrategyCheckerTest { private fun getTransformerClasses(): List> { val packageName = "com.github.jengelman.gradle.plugins.shadow.transformers" - val packagePath = packageName.replace('.', '/') + val parentClass = ResourceTransformer::class + val packagePath = packageName.replace('.', File.separatorChar) val classLoader = Thread.currentThread().contextClassLoader ?: ResourceTransformer::class.java.classLoader val resources = classLoader.getResources(packagePath) val classes = mutableListOf>() + val block = { className: String -> + runCatching { + val clazz = Class.forName(className) + with(clazz.kotlin) { + if (isSubclassOf(parentClass) && !isAbstract) { + @Suppress("UNCHECKED_CAST") classes.add(clazz as Class) + } + } + } + } for (url in resources) { when (url.protocol) { "file" -> { @@ -65,38 +74,18 @@ private fun getTransformerClasses(): List> { .forEach { file -> val relativePath = file.pathString.substringAfter(packagePath).removeSuffix(".class") val className = packageName + relativePath.replace(File.separatorChar, '.') - try { - val clazz = Class.forName(className) - if ( - ResourceTransformer::class.java.isAssignableFrom(clazz) && - !clazz.isInterface && - !Modifier.isAbstract(clazz.modifiers) - ) { - @Suppress("UNCHECKED_CAST") classes.add(clazz as Class) - } - } catch (_: Exception) {} + block(className) } } } "jar" -> { val connection = url.openConnection() as JarURLConnection val jarFile = connection.jarFile - val entries = jarFile.entries() - while (entries.hasMoreElements()) { - val entry = entries.nextElement() - val name = entry.name + jarFile.entries().toList().forEach { + val name = it.name if (name.startsWith(packagePath) && name.endsWith(".class")) { val className = name.removeSuffix(".class").replace('/', '.') - try { - val clazz = Class.forName(className) - if ( - ResourceTransformer::class.java.isAssignableFrom(clazz) && - !clazz.isInterface && - !Modifier.isAbstract(clazz.modifiers) - ) { - @Suppress("UNCHECKED_CAST") classes.add(clazz as Class) - } - } catch (_: Exception) {} + block(className) } } }