From 4802076bf36b894d8c729116825cbaba73dabd0b Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 22 Aug 2025 03:50:04 +0800 Subject: [PATCH 01/11] Mention `eachFile` and `filesNotMatching` --- docs/configuration/merging/README.md | 3 ++- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 15 ++++----------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/docs/configuration/merging/README.md b/docs/configuration/merging/README.md index 3a695752d..93340e7c0 100644 --- a/docs/configuration/merging/README.md +++ b/docs/configuration/merging/README.md @@ -85,7 +85,7 @@ to take: 1. Set the strategy to `INCLUDE` or `WARN`. 2. Apply your [`ResourceTransformer`][ResourceTransformer]s. 3. Remove duplicate entries by - - overriding the default strategy for specific files using [`filesMatching`][Jar.filesMatching] + - overriding the default strategy for specific files using [`filesMatching`][Jar.filesMatching] or [`eachFile`][Jar.eachFile] functions - or applying [`PreserveFirstFoundResourceTransformer`][PreserveFirstFoundResourceTransformer] for specific files - or write your own [`ResourceTransformer`][ResourceTransformer] to handle duplicates - or mechanism similar. @@ -451,6 +451,7 @@ It must be added using the [`transform`][ShadowJar.transform] methods. [AbstractCopyTask]: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.AbstractCopyTask.html +[Jar.eachFile]: https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:eachFile(org.gradle.api.Action) [Jar.filesMatching]: https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:filesMatching(java.lang.Iterable,%20org.gradle.api.Action) [AppendingTransformer]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-appending-transformer/index.html [DuplicatesStrategy]: https://docs.gradle.org/current/javadoc/org/gradle/api/file/DuplicatesStrategy.html diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index be28cf93c..cbe130cc9 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -219,19 +219,12 @@ public abstract class ShadowJar : Jar() { * **NOTE:** The strategy takes precedence over transforming and relocating. * Some [ResourceTransformer]s like [ServiceFileTransformer] will not work as expected with setting the strategy to * [EXCLUDE] (the default), as the duplicate resource files fed for them are excluded beforehand. - * Want [ResourceTransformer]s and the strategy to work together? There are several steps to take: - * - * 1. Set the strategy to [INCLUDE] or [WARN]. - * 2. Apply your [ResourceTransformer]s. - * 3. Remove duplicate entries by - * - overriding the default strategy for specific files using [filesMatching] - * - or applying `PreserveFirstFoundResourceTransformer` for specific files - * - or write your own `ResourceTransformer`s to handle duplicates - * - or mechanism similar. - * 4. Optionally, enable [failOnDuplicateEntries] to check duplicate entries in the final JAR. - * 5. Optionally, use [Diffuse](https://github.com/JakeWharton/diffuse) to diff the JARs. + * Want [ResourceTransformer]s and the strategy to work together? See more details in the + * [Handling Duplicates Strategy](https://gradleup.com/shadow/configuration/merging/#handling-duplicates-strategy) section. * + * @see [eachFile] * @see [filesMatching] + * @see [filesNotMatching] * @see [DuplicatesStrategy] * @see [CopySpec.duplicatesStrategy] */ From e7c8aa2c6b5369f205c07bd1eeceb9bce0543cfa Mon Sep 17 00:00:00 2001 From: Goooler Date: Thu, 28 Aug 2025 14:43:14 +0800 Subject: [PATCH 02/11] New steps --- docs/configuration/merging/README.md | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/docs/configuration/merging/README.md b/docs/configuration/merging/README.md index 93340e7c0..99f1eae41 100644 --- a/docs/configuration/merging/README.md +++ b/docs/configuration/merging/README.md @@ -77,21 +77,33 @@ Different strategies will lead to different results for `foo/bar` files in the J ``` The [`ResourceTransformer`][ResourceTransformer]s like [`ServiceFileTransformer`][ServiceFileTransformer] will not work -as expected as the duplicate resource files fed for them are excluded beforehand. However, this behavior might be what you expected for duplicate `foo/bar` files, preventing them from being included. +as expected as the duplicate resource files fed for them are excluded beforehand. However, this behavior might be what +you expected for duplicate `foo/bar` files, preventing them from being included. -Want [`ResourceTransformer`][ResourceTransformer]s and `duplicatesStrategy` to work together? There are several steps -to take: +Want [`ResourceTransformer`][ResourceTransformer]s and `duplicatesStrategy` to work together? There are several common +steps to take: -1. Set the strategy to `INCLUDE` or `WARN`. +1. Set the default strategy to `INCLUDE` or `WARN`. 2. Apply your [`ResourceTransformer`][ResourceTransformer]s. 3. Remove duplicate entries by - - overriding the default strategy for specific files using [`filesMatching`][Jar.filesMatching] or [`eachFile`][Jar.eachFile] functions + - overriding the default strategy for specific files to `EXCLUDE` or `FAIL` using + [`filesMatching`][Jar.filesMatching], [`filesNotMatching`][Jar.filesNotMatching], or [`eachFile`][Jar.eachFile] functions - or applying [`PreserveFirstFoundResourceTransformer`][PreserveFirstFoundResourceTransformer] for specific files - or write your own [`ResourceTransformer`][ResourceTransformer] to handle duplicates - or mechanism similar. 4. Optionally, enable [`ShadowJar.failOnDuplicateEntries`][ShadowJar.failOnDuplicateEntries] to check duplicate entries in the final JAR. 5. Optionally, use [Diffuse](https://github.com/JakeWharton/diffuse) to diff the JARs. +or the steps like: + +1. Set the default strategy to `EXCLUDE` or `FAIL`. +2. Apply your [`ResourceTransformer`][ResourceTransformer]s. +3. Bypass the duplicate entries which should be handled by the [`ResourceTransformer`][ResourceTransformer]s using + [`filesMatching`][Jar.filesMatching], [`filesNotMatching`][Jar.filesNotMatching], or [`eachFile`][Jar.eachFile] functions + to set their `duplicatesStrategy` to `INCLUDE` or `WARN`. +4. Optionally, enable [`ShadowJar.failOnDuplicateEntries`][ShadowJar.failOnDuplicateEntries] to check duplicate entries in the final JAR. +5. Optionally, use [Diffuse](https://github.com/JakeWharton/diffuse) to diff the JARs. + ## Basic ResourceTransformer Usage For simpler use cases, you can create a basic transformer: @@ -453,6 +465,7 @@ It must be added using the [`transform`][ShadowJar.transform] methods. [AbstractCopyTask]: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.AbstractCopyTask.html [Jar.eachFile]: https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:eachFile(org.gradle.api.Action) [Jar.filesMatching]: https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:filesMatching(java.lang.Iterable,%20org.gradle.api.Action) +[Jar.filesNotMatching]: https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:filesNotMatching(java.lang.Iterable,%20org.gradle.api.Action) [AppendingTransformer]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-appending-transformer/index.html [DuplicatesStrategy]: https://docs.gradle.org/current/javadoc/org/gradle/api/file/DuplicatesStrategy.html [GroovyExtensionModuleTransformer]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-groovy-extension-module-transformer/index.html From 5dde3270a9f227e58207600c5b7e3690eaa7acbe Mon Sep 17 00:00:00 2001 From: Goooler Date: Thu, 28 Aug 2025 14:56:28 +0800 Subject: [PATCH 03/11] Test `strategyExcludeCanBeOverriddenByEachFile` --- .../ServiceFileTransformerTest.kt | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) 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 edc224d9e..1150d829b 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 @@ -262,6 +262,29 @@ class ServiceFileTransformerTest : BaseTransformerTest() { } } + @Test + fun strategyExcludeCanBeOverriddenByEachFile() { + writeDuplicatesStrategy(DuplicatesStrategy.EXCLUDE) + projectScript.appendText( + """ + $shadowJarTask { + eachFile { + if (path == '$ENTRY_SERVICES_SHADE') { + duplicatesStrategy = DuplicatesStrategy.INCLUDE + } + } + } + """.trimIndent(), + ) + + run(shadowJarPath) + + assertThat(outputShadowedJar).useAll { + getContent(ENTRY_SERVICES_SHADE).isEqualTo(CONTENT_ONE_TWO) + getContent(ENTRY_SERVICES_FOO).isEqualTo("one") + } + } + private fun writeDuplicatesStrategy(strategy: DuplicatesStrategy) { projectScript.appendText( """ From ac7e13920ba70d4cb72c9cf5b5e398bdd78cb554 Mon Sep 17 00:00:00 2001 From: Goooler Date: Thu, 28 Aug 2025 14:58:08 +0800 Subject: [PATCH 04/11] Test `strategyIncludeCanBeOverriddenByEachFile` --- .../ServiceFileTransformerTest.kt | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) 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 1150d829b..c84d9ca47 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 @@ -285,6 +285,29 @@ class ServiceFileTransformerTest : BaseTransformerTest() { } } + @Test + fun strategyIncludeCanBeOverriddenByEachFile() { + writeDuplicatesStrategy(DuplicatesStrategy.INCLUDE) + projectScript.appendText( + """ + $shadowJarTask { + eachFile { + if (path == '$ENTRY_SERVICES_FOO') { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + } + } + } + """.trimIndent(), + ) + + run(shadowJarPath) + + assertThat(outputShadowedJar).useAll { + getContent(ENTRY_SERVICES_SHADE).isEqualTo(CONTENT_ONE_TWO) + getContent(ENTRY_SERVICES_FOO).isEqualTo("one") + } + } + private fun writeDuplicatesStrategy(strategy: DuplicatesStrategy) { projectScript.appendText( """ From 8ad8f73e9b560b703cf8023d98397ed64a25efe9 Mon Sep 17 00:00:00 2001 From: Goooler Date: Thu, 28 Aug 2025 15:16:57 +0800 Subject: [PATCH 05/11] Use `ParameterizedTest` for `strategyCanBeOverriddenByEachFile` --- .../ServiceFileTransformerTest.kt | 54 +++++++++---------- 1 file changed, 25 insertions(+), 29 deletions(-) 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 c84d9ca47..99b66a273 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 @@ -262,38 +262,20 @@ class ServiceFileTransformerTest : BaseTransformerTest() { } } - @Test - fun strategyExcludeCanBeOverriddenByEachFile() { - writeDuplicatesStrategy(DuplicatesStrategy.EXCLUDE) - projectScript.appendText( - """ - $shadowJarTask { - eachFile { - if (path == '$ENTRY_SERVICES_SHADE') { - duplicatesStrategy = DuplicatesStrategy.INCLUDE - } - } - } - """.trimIndent(), - ) - - run(shadowJarPath) - - assertThat(outputShadowedJar).useAll { - getContent(ENTRY_SERVICES_SHADE).isEqualTo(CONTENT_ONE_TWO) - getContent(ENTRY_SERVICES_FOO).isEqualTo("one") - } - } - - @Test - fun strategyIncludeCanBeOverriddenByEachFile() { - writeDuplicatesStrategy(DuplicatesStrategy.INCLUDE) + @ParameterizedTest + @MethodSource("eachFileStrategyProvider") + fun strategyCanBeOverriddenByEachFile( + default: DuplicatesStrategy, + override: DuplicatesStrategy, + matchPath: String, + ) { + writeDuplicatesStrategy(default) projectScript.appendText( """ $shadowJarTask { eachFile { - if (path == '$ENTRY_SERVICES_FOO') { - duplicatesStrategy = DuplicatesStrategy.EXCLUDE + if (path == '$matchPath') { + duplicatesStrategy = DuplicatesStrategy.$override } } } @@ -322,7 +304,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() { ) } - private companion object { + companion object { @JvmStatic fun withThrowingProvider() = listOf( Arguments.of( @@ -341,5 +323,19 @@ class ServiceFileTransformerTest : BaseTransformerTest() { Arguments.of(DuplicatesStrategy.INCLUDE, CONTENT_ONE_TWO, "one\ntwo"), Arguments.of(DuplicatesStrategy.WARN, CONTENT_ONE_TWO, "one\ntwo"), ) + + @JvmStatic + fun eachFileStrategyProvider() = listOf( + Arguments.of( + DuplicatesStrategy.EXCLUDE, + DuplicatesStrategy.INCLUDE, + ENTRY_SERVICES_SHADE, + ), + Arguments.of( + DuplicatesStrategy.INCLUDE, + DuplicatesStrategy.EXCLUDE, + ENTRY_SERVICES_FOO, + ), + ) } } From 37f53ae60c6862eea80f19c6a70347f547ba74c4 Mon Sep 17 00:00:00 2001 From: Goooler Date: Thu, 28 Aug 2025 15:19:28 +0800 Subject: [PATCH 06/11] Cleanups --- .../ServiceFileTransformerTest.kt | 41 ++++++++----------- 1 file changed, 16 insertions(+), 25 deletions(-) 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 99b66a273..7f6934f03 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 @@ -8,6 +8,11 @@ import com.github.jengelman.gradle.plugins.shadow.util.getContent import kotlin.io.path.appendText import kotlin.io.path.writeText import org.gradle.api.file.DuplicatesStrategy +import org.gradle.api.file.DuplicatesStrategy.EXCLUDE +import org.gradle.api.file.DuplicatesStrategy.FAIL +import org.gradle.api.file.DuplicatesStrategy.INCLUDE +import org.gradle.api.file.DuplicatesStrategy.INHERIT +import org.gradle.api.file.DuplicatesStrategy.WARN import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments @@ -221,8 +226,8 @@ class ServiceFileTransformerTest : BaseTransformerTest() { } @Test - fun strategyExcludeCanBeOverriddenByFilesMatching() { - writeDuplicatesStrategy(DuplicatesStrategy.EXCLUDE) + fun strategyCanBeOverriddenByFilesMatching() { + writeDuplicatesStrategy(EXCLUDE) projectScript.appendText( """ $shadowJarTask { @@ -242,8 +247,8 @@ class ServiceFileTransformerTest : BaseTransformerTest() { } @Test - fun strategyIncludeCanBeOverriddenByFilesNotMatching() { - writeDuplicatesStrategy(DuplicatesStrategy.INCLUDE) + fun strategyCanBeOverriddenByFilesNotMatching() { + writeDuplicatesStrategy(INCLUDE) projectScript.appendText( """ $shadowJarTask { @@ -307,35 +312,21 @@ class ServiceFileTransformerTest : BaseTransformerTest() { companion object { @JvmStatic fun withThrowingProvider() = listOf( - Arguments.of( - DuplicatesStrategy.FAIL, - "Cannot copy zip entry .* to .* because zip entry .* has already been copied there", - ), - Arguments.of( - DuplicatesStrategy.INHERIT, - "Entry .* is a duplicate but no duplicate handling strategy has been set", - ), + Arguments.of(FAIL, "Cannot copy zip entry .* to .* because zip entry .* has already been copied there"), + Arguments.of(INHERIT, "Entry .* is a duplicate but no duplicate handling strategy has been set"), ) @JvmStatic fun withoutThrowingProvider() = listOf( - Arguments.of(DuplicatesStrategy.EXCLUDE, CONTENT_ONE, "one"), - Arguments.of(DuplicatesStrategy.INCLUDE, CONTENT_ONE_TWO, "one\ntwo"), - Arguments.of(DuplicatesStrategy.WARN, CONTENT_ONE_TWO, "one\ntwo"), + Arguments.of(EXCLUDE, CONTENT_ONE, "one"), + Arguments.of(INCLUDE, CONTENT_ONE_TWO, "one\ntwo"), + Arguments.of(WARN, CONTENT_ONE_TWO, "one\ntwo"), ) @JvmStatic fun eachFileStrategyProvider() = listOf( - Arguments.of( - DuplicatesStrategy.EXCLUDE, - DuplicatesStrategy.INCLUDE, - ENTRY_SERVICES_SHADE, - ), - Arguments.of( - DuplicatesStrategy.INCLUDE, - DuplicatesStrategy.EXCLUDE, - ENTRY_SERVICES_FOO, - ), + Arguments.of(EXCLUDE, INCLUDE, ENTRY_SERVICES_SHADE), + Arguments.of(INCLUDE, EXCLUDE, ENTRY_SERVICES_FOO), ) } } From 0bc72e82298ea4591c2857cb1509197bc7db6fc3 Mon Sep 17 00:00:00 2001 From: Goooler Date: Thu, 28 Aug 2025 16:01:30 +0800 Subject: [PATCH 07/11] Add more examples --- docs/configuration/merging/README.md | 119 ++++++++++++++++++++++++++- 1 file changed, 115 insertions(+), 4 deletions(-) diff --git a/docs/configuration/merging/README.md b/docs/configuration/merging/README.md index 99f1eae41..d0e8bb567 100644 --- a/docs/configuration/merging/README.md +++ b/docs/configuration/merging/README.md @@ -91,8 +91,6 @@ steps to take: - or applying [`PreserveFirstFoundResourceTransformer`][PreserveFirstFoundResourceTransformer] for specific files - or write your own [`ResourceTransformer`][ResourceTransformer] to handle duplicates - or mechanism similar. -4. Optionally, enable [`ShadowJar.failOnDuplicateEntries`][ShadowJar.failOnDuplicateEntries] to check duplicate entries in the final JAR. -5. Optionally, use [Diffuse](https://github.com/JakeWharton/diffuse) to diff the JARs. or the steps like: @@ -101,8 +99,121 @@ or the steps like: 3. Bypass the duplicate entries which should be handled by the [`ResourceTransformer`][ResourceTransformer]s using [`filesMatching`][Jar.filesMatching], [`filesNotMatching`][Jar.filesNotMatching], or [`eachFile`][Jar.eachFile] functions to set their `duplicatesStrategy` to `INCLUDE` or `WARN`. -4. Optionally, enable [`ShadowJar.failOnDuplicateEntries`][ShadowJar.failOnDuplicateEntries] to check duplicate entries in the final JAR. -5. Optionally, use [Diffuse](https://github.com/JakeWharton/diffuse) to diff the JARs. + +then apply the optional steps if you want: + +- Enable [`ShadowJar.failOnDuplicateEntries`][ShadowJar.failOnDuplicateEntries] to check duplicate entries in the final JAR. +- Use [Diffuse](https://github.com/JakeWharton/diffuse) to diff the JARs. + +Here are some examples: + +=== "Kotlin" + + ```kotlin + // Using `filesNotMatching`: + tasks.shadowJar { + // Step 1. + duplicatesStrategy = DuplicatesStrategy.INCLUDE // Or WARN. + // Step 2. + mergeServiceFiles() + // Step 3. + filesNotMatching("META-INF/services/**") { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE // Or FAIL. + } + } + + // Using `filesMatching`: + tasks.shadowJar { + // Step 1. + duplicatesStrategy = DuplicatesStrategy.EXCLUDE // Or FAIL. + // Step 2. + mergeServiceFiles() + // Step 3. + filesMatching("META-INF/services/**") { + duplicatesStrategy = DuplicatesStrategy.INCLUDE // Or WARN. + } + } + + // Using `PreserveFirstFoundResourceTransformer`: + tasks.shadowJar { + // Step 1. + duplicatesStrategy = DuplicatesStrategy.INCLUDE // Or WARN. + // Step 2. + mergeServiceFiles() + // Step 3. + transform() { + resources.add("META-INF/foo/**") // Or something else where the first occurrence should be preserved. + } + } + + // Using `eachFile`: + tasks.shadowJar { + // Step 1. + duplicatesStrategy = DuplicatesStrategy.EXCLUDE // Or FAIL. + // Step 2. + mergeServiceFiles() + // Step 3. + eachFile { + if (path.startsWith("META-INF/services/")) { + duplicatesStrategy = DuplicatesStrategy.INCLUDE // Or WARN. + } + } + } + ``` + +=== "Groovy" + + ```groovy + // Using `filesNotMatching`: + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + // Step 1. + duplicatesStrategy = DuplicatesStrategy.INCLUDE // Or WARN. + // Step 2. + mergeServiceFiles() + // Step 3. + filesNotMatching('META-INF/services/**') { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE // Or FAIL. + } + } + + // Using `filesMatching`: + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + // Step 1. + duplicatesStrategy = DuplicatesStrategy.EXCLUDE // Or FAIL. + // Step 2. + mergeServiceFiles() + // Step 3. + filesMatching('META-INF/services/**') { + duplicatesStrategy = DuplicatesStrategy.INCLUDE // Or WARN. + } + } + + // Using `PreserveFirstFoundResourceTransformer`: + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + // Step 1. + duplicatesStrategy = DuplicatesStrategy.INCLUDE // Or WARN. + // Step 2. + mergeServiceFiles() + // Step 3. + transform(com.github.jengelman.gradle.plugins.shadow.transformers.PreserveFirstFoundResourceTransformer) { + resources.add('META-INF/foo/**') // Or something else where the first occurrence should be preserved. + } + } + + // Using `eachFile`: + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + // Step 1. + duplicatesStrategy = DuplicatesStrategy.EXCLUDE // Or FAIL. + // Step 2. + mergeServiceFiles() + // Step 3. + eachFile { + if (it.path.startsWith('META-INF/services/')) { + it.duplicatesStrategy = DuplicatesStrategy.INCLUDE // Or WARN. + } + } + } + ``` ## Basic ResourceTransformer Usage From a751768ccab5923a43305de36a94cf47f0d354e8 Mon Sep 17 00:00:00 2001 From: Goooler Date: Thu, 28 Aug 2025 16:03:56 +0800 Subject: [PATCH 08/11] Merge duplicate steps --- docs/configuration/merging/README.md | 64 ++++++---------------------- 1 file changed, 14 insertions(+), 50 deletions(-) diff --git a/docs/configuration/merging/README.md b/docs/configuration/merging/README.md index d0e8bb567..6096ceec2 100644 --- a/docs/configuration/merging/README.md +++ b/docs/configuration/merging/README.md @@ -110,49 +110,31 @@ Here are some examples: === "Kotlin" ```kotlin - // Using `filesNotMatching`: tasks.shadowJar { // Step 1. duplicatesStrategy = DuplicatesStrategy.INCLUDE // Or WARN. // Step 2. mergeServiceFiles() - // Step 3. + // Step 3. Using `filesNotMatching`: filesNotMatching("META-INF/services/**") { duplicatesStrategy = DuplicatesStrategy.EXCLUDE // Or FAIL. } - } - - // Using `filesMatching`: - tasks.shadowJar { - // Step 1. - duplicatesStrategy = DuplicatesStrategy.EXCLUDE // Or FAIL. - // Step 2. - mergeServiceFiles() - // Step 3. - filesMatching("META-INF/services/**") { - duplicatesStrategy = DuplicatesStrategy.INCLUDE // Or WARN. - } - } - - // Using `PreserveFirstFoundResourceTransformer`: - tasks.shadowJar { - // Step 1. - duplicatesStrategy = DuplicatesStrategy.INCLUDE // Or WARN. - // Step 2. - mergeServiceFiles() - // Step 3. + // Step 3. Using `PreserveFirstFoundResourceTransformer`: transform() { resources.add("META-INF/foo/**") // Or something else where the first occurrence should be preserved. } } - // Using `eachFile`: tasks.shadowJar { // Step 1. duplicatesStrategy = DuplicatesStrategy.EXCLUDE // Or FAIL. // Step 2. mergeServiceFiles() - // Step 3. + // Step 3. Using `filesMatching`: + filesMatching("META-INF/services/**") { + duplicatesStrategy = DuplicatesStrategy.INCLUDE // Or WARN. + } + // Step 3. Using `eachFile`: eachFile { if (path.startsWith("META-INF/services/")) { duplicatesStrategy = DuplicatesStrategy.INCLUDE // Or WARN. @@ -164,49 +146,31 @@ Here are some examples: === "Groovy" ```groovy - // Using `filesNotMatching`: tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { // Step 1. duplicatesStrategy = DuplicatesStrategy.INCLUDE // Or WARN. // Step 2. mergeServiceFiles() - // Step 3. + // Step 3. Using `filesNotMatching`: filesNotMatching('META-INF/services/**') { duplicatesStrategy = DuplicatesStrategy.EXCLUDE // Or FAIL. } - } - - // Using `filesMatching`: - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - // Step 1. - duplicatesStrategy = DuplicatesStrategy.EXCLUDE // Or FAIL. - // Step 2. - mergeServiceFiles() - // Step 3. - filesMatching('META-INF/services/**') { - duplicatesStrategy = DuplicatesStrategy.INCLUDE // Or WARN. - } - } - - // Using `PreserveFirstFoundResourceTransformer`: - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - // Step 1. - duplicatesStrategy = DuplicatesStrategy.INCLUDE // Or WARN. - // Step 2. - mergeServiceFiles() - // Step 3. + // Step 3. Using `PreserveFirstFoundResourceTransformer`: transform(com.github.jengelman.gradle.plugins.shadow.transformers.PreserveFirstFoundResourceTransformer) { resources.add('META-INF/foo/**') // Or something else where the first occurrence should be preserved. } } - // Using `eachFile`: tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { // Step 1. duplicatesStrategy = DuplicatesStrategy.EXCLUDE // Or FAIL. // Step 2. mergeServiceFiles() - // Step 3. + // Step 3. Using `filesMatching`: + filesMatching('META-INF/services/**') { + duplicatesStrategy = DuplicatesStrategy.INCLUDE // Or WARN. + } + // Step 3. Using `eachFile`: eachFile { if (it.path.startsWith('META-INF/services/')) { it.duplicatesStrategy = DuplicatesStrategy.INCLUDE // Or WARN. From 5e46de771a796e3f88bdc9319647b4b3f994fa20 Mon Sep 17 00:00:00 2001 From: Goooler Date: Thu, 28 Aug 2025 16:05:25 +0800 Subject: [PATCH 09/11] Add optional step at the last --- docs/configuration/merging/README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/configuration/merging/README.md b/docs/configuration/merging/README.md index 6096ceec2..4e924f15e 100644 --- a/docs/configuration/merging/README.md +++ b/docs/configuration/merging/README.md @@ -141,6 +141,11 @@ Here are some examples: } } } + + tasks.shadowJar { + // Optional step. + failOnDuplicateEntries = true + } ``` === "Groovy" @@ -177,6 +182,11 @@ Here are some examples: } } } + + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + // Optional step. + failOnDuplicateEntries = true + } ``` ## Basic ResourceTransformer Usage From d40a655c20d7d3bf28b61f310714ad894ac43180 Mon Sep 17 00:00:00 2001 From: Goooler Date: Thu, 28 Aug 2025 16:12:51 +0800 Subject: [PATCH 10/11] Refine --- docs/configuration/merging/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/configuration/merging/README.md b/docs/configuration/merging/README.md index 4e924f15e..f332acd8a 100644 --- a/docs/configuration/merging/README.md +++ b/docs/configuration/merging/README.md @@ -92,7 +92,7 @@ steps to take: - or write your own [`ResourceTransformer`][ResourceTransformer] to handle duplicates - or mechanism similar. -or the steps like: +Alternatively, you can follow these steps: 1. Set the default strategy to `EXCLUDE` or `FAIL`. 2. Apply your [`ResourceTransformer`][ResourceTransformer]s. @@ -100,7 +100,7 @@ or the steps like: [`filesMatching`][Jar.filesMatching], [`filesNotMatching`][Jar.filesNotMatching], or [`eachFile`][Jar.eachFile] functions to set their `duplicatesStrategy` to `INCLUDE` or `WARN`. -then apply the optional steps if you want: +Optional steps: - Enable [`ShadowJar.failOnDuplicateEntries`][ShadowJar.failOnDuplicateEntries] to check duplicate entries in the final JAR. - Use [Diffuse](https://github.com/JakeWharton/diffuse) to diff the JARs. From 7a164da6271f8a27f603b342ccd7eb4afafab6cf Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 28 Aug 2025 16:16:51 +0800 Subject: [PATCH 11/11] Update src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt --- .../plugins/shadow/transformers/ServiceFileTransformerTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 7f6934f03..6b1d2fe41 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 @@ -309,7 +309,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() { ) } - companion object { + private companion object { @JvmStatic fun withThrowingProvider() = listOf( Arguments.of(FAIL, "Cannot copy zip entry .* to .* because zip entry .* has already been copied there"),