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/docs/changes/README.md b/docs/changes/README.md index 56ed27f36..6affe8a1e 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 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/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..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 @@ -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 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.", + ) + } 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..021545c4f --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DuplicatesStrategyChecker.kt @@ -0,0 +1,33 @@ +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 +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 + element.duplicatesStrategy == DuplicatesStrategy.EXCLUDE -> { + val logger = Logging.getLogger(this::class.java) + logger.warn( + """ + '${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. + """ + .trimIndent() + ) + } + else -> Unit + } +} 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/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/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/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/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/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/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/")) 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) { 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..6696d3628 --- /dev/null +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DuplicatesStrategyCheckerTest.kt @@ -0,0 +1,95 @@ +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.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 kotlin.reflect.full.isSubclassOf +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 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" -> { + 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, '.') + block(className) + } + } + } + "jar" -> { + val connection = url.openConnection() as JarURLConnection + val jarFile = connection.jarFile + jarFile.entries().toList().forEach { + val name = it.name + if (name.startsWith(packagePath) && name.endsWith(".class")) { + val className = name.removeSuffix(".class").replace('/', '.') + block(className) + } + } + } + } + } + return classes +}