diff --git a/docs/changes/README.md b/docs/changes/README.md index 05092fb79..9fbe75076 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -3,6 +3,11 @@ ## [Unreleased](https://github.com/GradleUp/shadow/compare/9.1.0...HEAD) - 2025-xx-xx +### Changed + +- Merge Gradle Module descriptors into the modern `META-INF` path. ([#1706](https://github.com/GradleUp/shadow/pull/1706)) + The Gradle Module descriptors (`org.codehaus.groovy.runtime.ExtensionModule` files) defined under `META-INF/services/` + and `META-INF/groovy` will be merged into `META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule`. ## [9.1.0](https://github.com/GradleUp/shadow/compare/9.1.0) - 2025-08-29 diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt index 5ed589c12..898b8df69 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt @@ -13,8 +13,9 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionMo import com.github.jengelman.gradle.plugins.shadow.util.getContent import java.nio.file.Path import kotlin.io.path.appendText -import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource import org.junit.jupiter.params.provider.ValueSource class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { @@ -39,23 +40,27 @@ class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { run(shadowJarPath) - commonAssertions(PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR) + commonAssertions() } - @Test - fun groovyExtensionModuleTransformerWorksForLegacyGroovy() { + @ParameterizedTest + @MethodSource("resourcePathProvider") + fun mergeLegacyAndModernModuleDescriptorsIntoTheNewResourcePath( + fooEntry: String, + barEntry: String, + ) { projectScript.appendText( transform( dependenciesBlock = implementationFiles( - buildJarFoo(PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR), - buildJarBar(PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR), + buildJarFoo(fooEntry), + buildJarBar(barEntry), ), ), ) run(shadowJarPath) - commonAssertions(PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR) + commonAssertions() } private fun buildJarFoo( @@ -86,8 +91,8 @@ class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { ) } - private fun commonAssertions(entry: String) { - val properties = outputShadowedJar.use { it.getContent(entry) }.toProperties() + private fun commonAssertions() { + val properties = outputShadowedJar.use { it.getContent(PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR) }.toProperties() assertThat(properties.getProperty(KEY_MODULE_NAME)).isEqualTo(MERGED_MODULE_NAME) assertThat(properties.getProperty(KEY_MODULE_VERSION)).isEqualTo(MERGED_MODULE_VERSION) @@ -102,5 +107,13 @@ class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { const val EXTENSION_CLASSES_BAR = "com.acme.bar.SomeExtension,com.acme.bar.AnotherExtension" const val STATIC_EXTENSION_CLASSES_FOO = "com.acme.foo.FooStaticExtension" const val STATIC_EXTENSION_CLASSES_BAR = "com.acme.bar.SomeStaticExtension" + + @JvmStatic + fun resourcePathProvider() = listOf( + Arguments.of(PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR, PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR), + Arguments.of(PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR, PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR), + Arguments.of(PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR, PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR), + Arguments.of(PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR, PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR), + ) } } 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 887e485fb..885dfcf67 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 @@ -21,26 +21,15 @@ import org.gradle.api.file.FileTreeElement * files came from the legacy location, otherwise it will be written into the now standard location (META-INF/groovy). * Note that certain JDK9+ tooling will break when using the legacy location. * - * Modified from [eu.appsatori.gradle.fatjar.tasks.PrepareFiles.groovy](https://github.com/musketyr/gradle-fatjar-plugin/blob/master/src/main/groovy/eu/appsatori/gradle/fatjar/tasks/PrepareFiles.groovy). * Related to [org.apache.maven.plugins.shade.resource.GroovyResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/GroovyResourceTransformer.java). */ @CacheableTransformer public open class GroovyExtensionModuleTransformer : ResourceTransformer { private val module = Properties() - /** - * default to Groovy 2.4 or earlier - */ - private var legacy = true - override fun canTransformResource(element: FileTreeElement): Boolean { val path = element.path - if (path == PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR) { - // Groovy 2.5+ - legacy = false - return true - } - return path == PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR + return path == PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR || path == PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR } override fun transform(context: TransformerContext) { @@ -64,26 +53,28 @@ public open class GroovyExtensionModuleTransformer : ResourceTransformer { } } - private fun handle(key: String, value: String, mergeValue: (String) -> Unit) { - val existingValue = module.getProperty(key) - if (existingValue != null) { - mergeValue(existingValue) - } else { - module.setProperty(key, value) - } - } - override fun hasTransformedResource(): Boolean = module.isNotEmpty() override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { - val name = if (legacy) PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR else PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR - os.putNextEntry(zipEntry(name, preserveFileTimestamps)) + os.putNextEntry(zipEntry(PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR, preserveFileTimestamps)) module.inputStream().use { it.copyTo(os) } os.closeEntry() } + private fun handle(key: String, value: String, mergeValue: (String) -> Unit) { + val existingValue = module.getProperty(key) + if (existingValue != null) { + mergeValue(existingValue) + } else { + module.setProperty(key, value) + } + } + + /* + * https://groovy-lang.org/metaprogramming.html#module-descriptor + */ public companion object { public const val PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR: String = "META-INF/services/org.codehaus.groovy.runtime.ExtensionModule"