Repository: GradleUp/shadow
Pull request #1255 (2025-02-17 02:55:27)

Add a test for non-cacheable transformer


Patch:
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 2beaf3ef..2160d3b4 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
@@ -25,6 +25,7 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer.Compa
 import java.io.File
 import java.io.IOException
 import java.util.jar.JarFile
+import kotlin.reflect.full.hasAnnotation
 import org.apache.tools.zip.ZipOutputStream
 import org.gradle.api.Action
 import org.gradle.api.artifacts.Configuration
@@ -63,8 +64,8 @@ public abstract class ShadowJar :
     manifest = DefaultInheritManifest(services.get(FileResolver::class.java))
 
     outputs.doNotCacheIf("Has one or more transforms or relocators that are not cacheable") {
-      transformers.get().any { !isCacheableTransform(it::class.java) } ||
-        relocators.get().any { !isCacheableRelocator(it::class.java) }
+      transformers.get().any { !it::class.hasAnnotation<CacheableTransformer>() } ||
+        relocators.get().any { !it::class.hasAnnotation<CacheableRelocator>() }
     }
   }
 
@@ -302,14 +303,6 @@ public abstract class ShadowJar :
     transformers.add(transformer)
   }
 
-  private fun isCacheableRelocator(clazz: Class<out Relocator>): Boolean {
-    return clazz.isAnnotationPresent(CacheableRelocator::class.java)
-  }
-
-  private fun isCacheableTransform(clazz: Class<out Transformer>): Boolean {
-    return clazz.isAnnotationPresent(CacheableTransformer::class.java)
-  }
-
   private val packageRelocators: List<SimpleRelocator>
     get() {
       if (!enableRelocation.get()) return emptyList()


Test patch:
diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt
index c6bce26f..5a527a21 100644
--- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt
+++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt
@@ -21,15 +21,7 @@ import org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE
 abstract class BaseCachingTest : BasePluginTest() {
   open val taskPath: String = shadowJarTask
 
-  fun assertExecutionSuccess(vararg outputs: String) {
-    // task was executed and not pulled from cache
-    assertRunWithResult(SUCCESS, outputs = outputs)
-  }
-
-  /**
-   * This should be called after [assertExecutionSuccess] to ensure that the [taskPath] is cached.
-   */
-  fun assertExecutionsFromCacheAndUpToDate(vararg outputs: String) {
+  fun cleanOutputs() {
     run("clean")
     // Make sure the output shadow jar has been deleted.
     assertFailure { outputShadowJar.close() }.isInstanceOf(NoSuchFileException::class)
@@ -37,7 +29,18 @@ abstract class BaseCachingTest : BasePluginTest() {
     val buildDirs = projectRoot.walk().filter { it.isDirectory() && it.name == "build" }
     // Make sure build folders are deleted by clean task.
     assertThat(buildDirs).isEmpty()
+  }
 
+  fun assertExecutionSuccess(vararg outputs: String) {
+    // The task was executed and not pulled from cache.
+    assertRunWithResult(SUCCESS, outputs = outputs)
+  }
+
+  /**
+   * This should be called after [assertExecutionSuccess] to ensure that the [taskPath] is cached.
+   */
+  fun assertExecutionsFromCacheAndUpToDate(vararg outputs: String) {
+    cleanOutputs()
     // Run the task again to ensure it is pulled from cache.
     assertRunWithResult(FROM_CACHE, outputs = outputs)
     // Run the task again to ensure it is up-to-date.
diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt
index 2d11b0d5..245bb1d0 100644
--- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt
+++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt
@@ -12,6 +12,7 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.IncludeResourceTr
 import com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCacheFileTransformer
 import com.github.jengelman.gradle.plugins.shadow.transformers.ManifestAppenderTransformer
 import com.github.jengelman.gradle.plugins.shadow.transformers.ManifestResourceTransformer
+import com.github.jengelman.gradle.plugins.shadow.transformers.NoOpTransformer
 import com.github.jengelman.gradle.plugins.shadow.transformers.PropertiesFileTransformer
 import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer
 import com.github.jengelman.gradle.plugins.shadow.transformers.XmlAppendingTransformer
@@ -120,6 +121,43 @@ class TransformerCachingTest : BaseCachingTest() {
     assertions("baz")
   }
 
+  @Test
+  fun shadowJarCacheDisabledIfAnyTransformerIsNotCacheable() {
+    projectScriptPath.appendText(
+      """
+        $shadowJar {
+          mergeServiceFiles()
+        }
+      """.trimIndent() + System.lineSeparator(),
+    )
+
+    assertCompositeExecutions()
+
+    projectScriptPath.appendText(
+      """
+        $shadowJar {
+          mergeGroovyExtensionModules()
+        }
+      """.trimIndent() + System.lineSeparator(),
+    )
+
+    assertCompositeExecutions()
+
+    projectScriptPath.appendText(
+      """
+        $shadowJar {
+          // Use NoOpTransformer to mock a custom transformer here, it's not cacheable.
+          transform(${NoOpTransformer::class.java.name}.INSTANCE)
+        }
+      """.trimIndent(),
+    )
+
+    assertExecutionSuccess()
+    cleanOutputs()
+    // The shadowJar task should be executed again as the cache is disabled.
+    assertExecutionSuccess()
+  }
+
   @ParameterizedTest
   @MethodSource("transformerConfigProvider")
   fun shadowJarIsCachedCorrectlyWhenUsingOtherTransformers(pair: Pair<String, KClass<*>>) {