wujek
wujek

Reputation: 11070

Gradle plugins' transitive dependencies interfering and breaking the build

I am using Gradle 6.1 in a multimodule project. I am also using two plugins: kotlin("jvm") and id("com.google.cloud.tools.jib"), and they are loaded in the following modules:

root/
 build.gradle.kts loads kotlin("jvm")
 services/
   my-service/
     rest/
       build.gradle.kts loads id("com.google.cloud.tools.jib")

(There are more modules, files etc. but these are the relevant ones.)

The build fails:

$ ./gradlew clean jibDockerBuild
...
* What went wrong:
Execution failed for task ':services:driver:rest:jibDockerBuild'.
> com.google.cloud.tools.jib.plugins.common.BuildStepsExecutionException: 'org.apache.http.client.config.RequestConfig$Builder     
org.apache.http.client.config.RequestConfig$Builder.setNormalizeUri(boolean)'

I identified the issue: both the Kotlin and JIB plugins have a transitive dependency on org.apache.httpcomponents:httpclient: Kotlin requires 4.5.3 and JIB 4.5.10. The problem is, in this project setup only 4.5.3 is loaded, and JIB fails as the new method is not available. This can be checked with ./gradlew buildEnv.

I've found a workaround, I need to load both plugins at the root level (which one is first seems to be irrelevant) in the main Gradle file; now ./gradlew buildEnv shows that the higher dependency version is used, also for Kotlin (output shortened and incomplete):

classpath
+--- org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:1.3.61
|    \--- org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.61
|         +--- de.undercouch:gradle-download-task:3.4.3
|         |    \--- org.apache.httpcomponents:httpclient:4.5.3 -> 4.5.10

It works in this case, but it could be that the new library version breaks the Kotlin plugin. The problem is that the plugins and their dependencies are on the classpath without separation, something that was normal on Java before Jigsaw etc. Is there any way for Gradle to be able to separate the dependencies so that each plugin uses exactly the version it declares? I am building on Java 11, so the module system could be utilized, but does Gradle have an option to turn it on?

EDIT: updating to Kotlin 1.3.70 also fixes the issue as it doesn't depend on the library any longer. The general question is still valid, though.

Upvotes: 5

Views: 2439

Answers (1)

Cisco
Cisco

Reputation: 23060

Is there any way for Gradle to be able to separate the dependencies so that each plugin uses exactly the version it declares

No.

All plugins share the same build script configuration: classpath

It follows the same dependency resolution that application dependencies follow. So you can enforce that for this particular dependency only use a specific version always:

buildscript {
    configurations {
        classpath {
            resolutionStrategy {
                force("org.apache.httpcomponents:httpclient:4.5.10")
            }
        }
    }
}

That's just one of many ways you can take control of dependency resolution for build script dependencies. You could also use a platform to advise on the dependency versions:

buildscript {
    dependencies {
        classpath(enforcedPlatform("org.springframework.boot:spring-boot-dependencies:2.2.5.RELEASE"))
    }
}

Refer to the docs for more info:

Upvotes: 5

Related Questions