GradleUp / shadow

Gradle plugin to create fat/uber JARs, apply file transforms, and relocate packages for applications and libraries. Gradle version of Maven's Shade plugin.
https://www.gradleup.com/shadow/
Apache License 2.0
3.77k stars 396 forks source link

LinkageError and NSMException due to Kotlin library conflict in multi-module project with ShadowJar relocation #1010

Open Syrent opened 3 weeks ago

Syrent commented 3 weeks ago

Expected and Results

Expected: Relocating "kotlin" classes within the dependencies (e.g., "me.projectA.kotlin") should preserve all extension methods and classes, allowing the application to work seamlessly without missing methods or linkage issues.

Actual: When Project C is loaded in the classpath, it adds its own version of Kotlin, which conflicts with the other versions in Projects A and B. This results in LinkageError when Project B attempts to access extension methods from Project A. If I attempt to solve this with ShadowJar relocation (e.g., renaming "kotlin" to "me.projectA.kotlin"), a new issue arises: extension methods and classes are not accessible, causing a NoSuchMethodException. Additionally, the relocated jar file for Project A no longer includes extension methods, making them unavailable for indexing.

Example
If I add cloud-kotlin-extensions as a dependency in Project A and then relocate kotlin to projecta.kotlin, I encounter the following issue: when attempting to use any of the extensions from Cloud in Project B, these extensions do not load or index. Additionally, when inspecting Project A's classes, the MutableCommandBuilder.kt file (which contains the extensions) is missing, even though all other classes are still present.

Related environent and versions

Gradle versions: 8.8.x to 8.10.2 ShadowJar versions: 7.1 to 8.3.4 Kotlin versions: 1.7.20 to 2.0.21 Java versions: 1.17 and 1.21

Reproduction steps

  1. Create Projects A, B, and C using Kotlin.
  2. Define Project A with API methods, extensions, and Kotlin lambdas (e.g., () -> String or similar).
  3. Add Project A as an API dependency in Project B.
  4. Add Projects A, B, and C as dependencies in Project D, which includes all of them in its classpath (Project D adds other projects to classpath in runtime).
  5. Attempt to execute Project B using extension methods from Project A.
    • Observe LinkageError if Project C’s Kotlin classes override others.
  6. Use ShadowJar to relocate Kotlin in Projects A, B, and C to separate namespaces (e.g., me.projectA.kotlin).
    • Observe NoSuchMethodException when trying to use extension methods after relocation.

Anything else?

No response