Repository: GradleUp/shadow (kotlin)

Issue:
Circular shadowJar dependencies due to TARGET_JVM_VERSION_ATTRIBUTE
### Expected and Results

See https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/14388 It seems like when dependency is added with `xxx(project(":foo"))` it resolves to shadowjar output not the output of the regular jar task, so `xxx(project(":foo"))` behaves like `xxx(project(":foo", configuration = "shadow"))`.
We bisected the shadow plugin and found that removing https://github.com/GradleUp/shadow/blob/019ff7b1695aa5fbba573a10dfb629537d8676bb/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt#L73 fixes the issue. With that line removed the build will still fail because `mergeServiceFiles()` appears to work only with `duplicatesStrategy = DuplicatesStrategy.INCLUDE`. Not sure whether that is intentional or a bug.

### Related environment and versions

shadow plugin 9.0.1
gradle 8.14.3

### Reproduction steps

clone the branch used in https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/14388 and run `./gradlew assemble` or `./gradlew build -PskipTests=true`

### Anything else?

_No response_

Pull request description:
Closes #1665.

---

- [x] [CHANGELOG](https://github.com/GradleUp/shadow/blob/main/docs/changes/README.md)'s "Unreleased" section has been updated, if applicable.

Patch:
diff --git a/docs/changes/README.md b/docs/changes/README.md
index 74de2118d..b1970711f 100644
--- a/docs/changes/README.md
+++ b/docs/changes/README.md
@@ -3,6 +3,10 @@
 
 ## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.2...HEAD) - 2025-xx-xx
 
+## Changed
+
+- Don't inject `TargetJvmVersion` attribute when automatic JVM targeting is disabled. ([#1666](https://github.com/GradleUp/shadow/pull/1666))
+
 
 ## [9.0.2](https://github.com/GradleUp/shadow/releases/tag/9.0.2) - 2025-08-15
 
diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt
index f02c10db3..a3982d2c5 100644
--- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt
+++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt
@@ -51,7 +51,7 @@ public abstract class ShadowJavaPlugin @Inject constructor(
       compileClasspath.extendsFrom(shadowConfiguration)
     }
     @Suppress("EagerGradleConfiguration") // this should be created eagerly.
-    configurations.create(SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME) {
+    val shadowRuntimeElements = configurations.create(SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME) {
       it.extendsFrom(shadowConfiguration)
       it.isCanBeConsumed = true
       it.isCanBeResolved = false
@@ -63,17 +63,25 @@ public abstract class ShadowJavaPlugin @Inject constructor(
           objects.named(LibraryElements::class.java, LibraryElements.JAR),
         )
         attrs.attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling::class.java, Bundling.SHADOWED))
-        val targetJvmVersion = configurations.named(COMPILE_CLASSPATH_CONFIGURATION_NAME)
-          .map { compileClasspath ->
-            compileClasspath.attributes.getAttribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE)
-              ?: javaPluginExtension.targetCompatibility.majorVersion.toInt()
-          }
-
-        // Track JavaPluginExtension to update targetJvmVersion when it changes.
-        attrs.attributeProvider(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, targetJvmVersion)
       }
       it.outgoing.artifact(tasks.shadowJar)
     }
+
+    // Must use afterEvaluate here as we need to check the value of targetJvmVersion and track its changes.
+    afterEvaluate {
+      val targetJvmVersion = configurations.named(COMPILE_CLASSPATH_CONFIGURATION_NAME).map { compileClasspath ->
+        compileClasspath.attributes.getAttribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE)
+      }.getOrElse(javaPluginExtension.targetCompatibility.majorVersion.toInt())
+
+      // https://github.com/gradle/gradle/blob/4198ab0670df14af9f77b9098dc892b199ac1f3f/platforms/jvm/plugins-java-base/src/main/java/org/gradle/api/plugins/jvm/internal/DefaultJvmLanguageUtilities.java#L85-L87
+      if (targetJvmVersion == Int.MAX_VALUE) {
+        logger.info("Cannot set the target JVM version to Int.MAX_VALUE when `java.autoTargetJvmDisabled` is enabled or in other cases.")
+      } else {
+        shadowRuntimeElements.attributes { attrs ->
+          attrs.attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, targetJvmVersion)
+        }
+      }
+    }
   }
 
   protected open fun Project.configureComponents() {


Test patch:
diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt
index 64279fea4..3b650516e 100644
--- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt
+++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt
@@ -3,6 +3,7 @@ package com.github.jengelman.gradle.plugins.shadow
 import assertk.Assert
 import assertk.all
 import assertk.assertThat
+import assertk.assertions.contains
 import assertk.assertions.containsOnly
 import assertk.assertions.isEmpty
 import assertk.assertions.isEqualTo
@@ -127,6 +128,38 @@ class PublishingTest : BasePluginTest() {
     assertions(attrsWithoutTargetJvm + targetJvmAttr8)
   }
 
+  @Issue(
+    "https://github.com/GradleUp/shadow/issues/1665",
+  )
+  @Test
+  fun dontInjectTargetJvmVersionWhenAutoTargetJvmDisabled() {
+    projectScript.appendText(
+      publishConfiguration(
+        projectBlock = """
+          java {
+            disableAutoTargetJvm()
+          }
+        """.trimIndent(),
+        shadowBlock = """
+          archiveClassifier = ''
+          archiveBaseName = 'maven-all'
+        """.trimIndent(),
+      ),
+    )
+
+    val result = publish(infoArgument)
+
+    assertThat(result.output).contains(
+      "Cannot set the target JVM version to Int.MAX_VALUE when `java.autoTargetJvmDisabled` is enabled or in other cases.",
+    )
+    assertShadowVariantCommon(
+      gmm = gmmAdapter.fromJson(repoPath("my/maven-all/1.0/maven-all-1.0.module")),
+      variantAttrs = shadowVariantAttrs.filterNot { (name, _) ->
+        name == TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name
+      }.toTypedArray(),
+    )
+  }
+
   @Test
   fun publishShadowJarInsteadOfJar() {
     projectScript.appendText(
@@ -412,7 +445,7 @@ class PublishingTest : BasePluginTest() {
     return JarPath(remoteRepoPath.resolve(relative))
   }
 
-  private fun publish(): BuildResult = run("publish")
+  private fun publish(vararg arguments: String): BuildResult = run("publish", *arguments)
 
   private fun publishConfiguration(
     projectBlock: String = "",