Combine resolutionStrategy with exclusion - gradle

I am using Gradle 6.5.1. I have added a custom resolutionStrategy to a build.gradle file - but now an exclusion like this is not being applied:
testImplementation("com.example:foo-bar_2.12:$dependencies_version"){
exclude group: 'org.scala-lang', module: 'scala-library'
exclude group: 'org.typelevel', module: 'cats-core_2.12' // <- !! NOT WORKING !!
}
So it seems that custom resolutionStrategies and exclusions are not composable, at least not by default, in Gradle 6.5.1. Is there some way I can make Gradle fall back to its "default" resolutionStrategy if mine is not relevant? If not, what should I do?

Issue
You have to have some special resolutionStrategy in place in order to overwrite the exclusion for cats-core_2.12.
Or
Dependency on cats-core_2.12 is being resolved as transitive dependency from other dependency and not com.example:foo-bar_2.12 as you expect. You should use gradle dependency command and post here the result of where cats-core is being resolved.
Example
I have following simple build.gradle build script with similar exclusion rule and resolutionStrategy as you can see below and cats-core will still be excluded from dependencies as expected
dependencies {
testImplementation('com.github.julien-truffaut:monocle-core_2.13:3.0.0-M5'){
exclude group: 'org.scala-lang', module: 'scala-library'
exclude group: 'org.typelevel', module: 'cats-core_2.13' // <- WORKS
}
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
}
configurations.all {
resolutionStrategy {
failOnVersionConflict()
preferProjectModules()
force 'org.typelevel:cats-core_2.13:2.4.0'
// cache dynamic versions for 10 minutes
cacheDynamicVersionsFor 10*60, 'seconds'
// don't cache changing modules at all
cacheChangingModulesFor 0, 'seconds'
}
}
Dependencies:
Running following command ./gradlew dependencies
You can see that cats-core is not listed in dependencies as it's excluded.
...
testRuntimeClasspath - Runtime classpath of source set 'test'.
+--- com.github.julien-truffaut:monocle-core_2.13:3.0.0-M5
| \--- org.typelevel:cats-free_2.13:2.6.0
| \--- org.typelevel:simulacrum-scalafix-annotations_2.13:0.5.4
+--- org.junit.jupiter:junit-jupiter-api:5.7.0
| +--- org.junit:junit-bom:5.7.0
| | +--- org.junit.jupiter:junit-jupiter-api:5.7.0 (c)
...
Alternative
If exclusion in your case is not forking for specific dependency, maybe exclusion for all configurations might help like example below:
configurations {
all.collect { configuration ->
configuration.exclude group: 'org.typelevel', module: 'cats-core_2.13'
}
}

Related

Gradle Dependency of Dependency Substitution

I have a spring-boot gradle project. It has a dependency of a dependency of a dependency that I cannot use for internal reasons. I've forked that module and published it to my repo with its own version number that I want to be used in my project.
Part of the dependency tree looks like:
compileClasspath - Compile classpath for source set 'main'.
+--- org.springframework.boot:spring-boot-starter-web:2.7.2
| +--- org.springframework.boot:spring-boot-starter-tomcat:2.7.2
| | +--- jakarta.annotation:jakarta.annotation-api:1.3.5
| | +--- org.apache.tomcat.embed:tomcat-embed-core:9.0.65
| | +--- org.apache.tomcat.embed:tomcat-embed-el:9.0.65
| | \--- org.apache.tomcat.embed:tomcat-embed-websocket:9.0.65
I need to force my project to use a custom version of those bottom two modules, let's call the new versions org.apache.tomcat.embed:tomcat-embed-el:9.0.65.CUSTOM and org.apache.tomcat.embed:tomcat-embed-websocket:9.0.65.CUSTOM.
I've never attempted this. I'm trying all different setups in my project's build.gradle file like
configurations { main }
configurations.main.resolutionStrategy.dependencySubstitution {
substitute module('org.apache.tomcat.embed:tomcat-embed-el:9.0.65') using module('org.apache.tomcat.embed:tomcat-embed-el:9.0.65.CUSTOM')
substitute module('org.apache.tomcat.embed:tomcat-embed-websocket:9.0.65') using module('org.apache.tomcat.embed:tomcat-embed-websocket:9.0.65.CUSTOM')
}
But the 9.0.65 versions keep getting injected. So my question(s) are:
Am I using dependencySubstitution incorrectly, and where in the build.gradle should it be?
Does dependencySubstitution work for dependencies of dependencies (ad nauseam), or only for direct dependencies?
Short of forking all these dependencies to declare their dependencies, is there any way I can force my Gradle to never use version 9.0.65 and only ever use 9.0.65.CUSTOM no matter its location in the dependency tree?
Assuming your configurations are setup properly and the coordinates for your custom modules are correct, you should be able to do
allprojects {
configurations.all {
resolutionStrategy {
dependencySubstitution {
substitute(module("org.apache.tomcat.embed:tomcat-embed-el")).with(module("org.apache.tomcat.embed:tomcat-embed-el:9.0.65.CUSTOM"))
substitute(module("org.apache.tomcat.embed:tomcat-embed-websocket")).with(module("org.apache.tomcat.embed:tomcat-embed-websocket:9.0.65.CUSTOM"))
}
}
}
}
I would double check you have your repositories, configurations and that you are doing this dependency substitution in the module that needs to substitute the given modules for custom ones. Otherwise you may run into errors.

Gradle java-library dependency issues

I have developed a Java library using the java-library Gradle plugin. This library has e dependency on protobuf-java which I need to expose as a transitive dependency to users of the library.
I have the following snippets in my library's build.gradle
plugins {
id 'java-library'
id "maven-publish"
id "com.google.protobuf" version "0.8.16"
}
...
dependencies {
api ("com.google.protobuf:protobuf-java:3.17.3")
testImplementation("com.google.protobuf:protobuf-java-util:3.17.3")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.7.2")
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.7.2")
}
Running gradlew dependencies gives me the following as expected
api - API dependencies for source set 'main'. (n)
\--- com.google.protobuf:protobuf-java:3.17.3 (n)
But when I add the library as a dependency to in my main app, I don't get any transitive dependencies. Doing a gradlew dependencies in my main app gives me:
compileClasspath - Compile classpath for source set 'main'.
+--- my-library:my-library:1.0.21
+--- javax.jms:javax.jms-api -> 2.0.1
....
What could be the cause for the dependency not showing up in the main app?
The problem was with the maven-publish plugin.
I had to add the following to my publishing section to get it to work.
publications {
mavenJava(MavenPublication) { from project.components.java }
}

Specify Gradle version constraints for multiple configurations

I have a multi-module Gradle 6.5 project which also includes some test fixtures code. To avoid dependency issues I'd like to set (and maintain) the versions in one place, and just refer to the version-less dependencies in the individual modules:
subprojects {
apply plugin: 'java'
dependencies {
implementation 'com.google.guava:guava'
constraints {
implementation 'com.google.guava:guava:20.0'
compileOnly 'com.google.code.findbugs:jsr305:3.0.2'
}
}
}
Now, if a module includes compilyOnly 'com.google.code.findbugs:jsr305' the version 3.0.2 is taken based on the constraints above. This works fine if I know the configuration (implementation, compileOnly, ...).
The question now is: How can I specify versions that apply to all configuration? What if a module decides to use JSR305 annotations for test fixtures code? testFixtures 'com.google.code.findbugs:jsr305' fails, as the version is not specified anywhere. I also think it's a bad idea to repeat the version specification for all (possible) configuration:
implementation 'com.google.code.findbugs:jsr305:3.0.2'
testFixturesImplementation 'com.google.code.findbugs:jsr305:3.0.2'
compileOnly 'com.google.code.findbugs:jsr305:3.0.2'
Is there a shortcut to this?
If fixed this using the java-platform plugin.
Platform:
plugins {
id 'java-platform'
}
dependencies {
constraints {
api 'com.google.guava:guava:20.0'
api 'com.google.code.findbugs:jsr305:3.0.2'
}
}
Regular module with test fixtures:
plugins {
id "java-test-fixtures"
}
dependencies {
testFixturesImplementation platform(project(':platform-module'))
testFixturesCompileOnly 'com.google.code.findbugs:jsr305'
}
./gradlew -q module-with-test-fixtures:dependencies gives:
[...]
testFixturesCompileClasspath - Compile classpath for source set 'test fixtures'.
+--- project :module-with-test-fixtures (*)
+--- com.google.code.findbugs:jsr305 -> 3.0.2
\--- project :platform-module
\--- com.google.code.findbugs:jsr305:3.0.2 (c)
[...]
(c) - dependency constraint

Gradle - unable to track down transitive dependency

I have two modules: common and domain. Domain is a dependency of common. In domain, I'm trying to add the latest version of Spring Data Elasticsearch but it keeps reverting back to an old version. My domain's build.gradle file looks like this:
domain build.gradle
apply plugin: 'spring-boot'
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.3.3.RELEASE")
}
}
dependencies {
compile("org.springframework.boot:spring-boot-starter-data-jpa")
compile("org.springframework.boot:spring-boot-starter-redis")
compile("org.springframework.data:spring-data-elasticsearch:2.0.1.RELEASE")
compile 'org.slf4j:slf4j-api'
compile 'com.google.guava:guava:19.0'
compile 'com.google.code.gson:gson:2.4'
testCompile "org.mockito:mockito-core:1.+"
}
The version for elasticsearch here is 2.0.1.RELASE However, if I run dependencyInsight in common, it is retrieving 1.3.4.RELEASE instead:
gradle dependencyInsight --dependency elasticsearch --configuration compile
:common:dependencyInsight
Download https://repo1.maven.org/maven2/org/springframework/data/spring-data-elasticsearch/1.3.4.RELEASE/spring-data-elasticsearch-1.3.4.RELEASE.pom
org.elasticsearch:elasticsearch:1.5.2 (selected by rule)
\--- org.springframework.data:spring-data-elasticsearch:1.3.4.RELEASE
\--- project :domain
\--- compile
org.springframework.data:spring-data-elasticsearch:1.3.4.RELEASE (selected by rule)
org.springframework.data:spring-data-elasticsearch:2.0.1.RELEASE -> 1.3.4.RELEASE
\--- project :domain
\--- compile
common build.gradle
apply plugin: 'spring-boot'
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.3.3.RELEASE")
}
}
dependencies {
compile project(':domain')
compile 'com.google.code.gson:gson:2.4'
compile 'org.owasp.encoder:encoder:1.2'
compile 'com.ning:async-http-client:1.9.31'
compile 'org.slf4j:slf4j-api'
compile 'org.springframework.security:spring-security-core'
compile 'org.springframework.security:spring-security-acl:4.0.3.RELEASE'
compile 'javax.mail:javax.mail-api:1.5.4'
compile 'com.sun.mail:javax.mail:1.5.4'
testCompile group: 'junit', name: 'junit', version: '4.12'
testCompile "org.mockito:mockito-core:1.+"
}
Is there a reason why version 1.3.4.RELEASE is replacing 2.0.1.RELEASE?
You're applying Spring Boot's Gradle plugin to your common project. That means that its dependency management will be controlling the versions of the project's dependencies. To get the version of Spring Data Elasticsearch that you want, you can override Boot's dependency management by adding the following:
dependencyManagement {
dependencies {
dependency 'org.springframework.data:spring-data-elasticsearch:2.0.1.RELEASE'
}
}

Gradle dependency hierarchy in script

My goal is to print the dependencies of a gradle build including there hierarchy. The idea is to graphically build a dependency graph. The information I need would be the same as when I type gradle dependencies.
How can I achive this? Where do I get the information from, when I create my own task?
Perhaps this is what you're looking for:
project.configurations.compile.resolvedConfiguration.resolvedArtifacts.each {
println it.name // << the artifact name
println it.file // << the file reference
}
It comes from How to retrieve a list of actual dependencies (including transitive deps) - Old Forum - Gradle Forums
I'm just a gradle newbie, so perhaps there is depth to your question I don't see. Do you mean:
> gradle dependencies
:dependencies
------------------------------------------------------------
Root project
------------------------------------------------------------
archives - Configuration for archive artifacts.
No dependencies
compile - Compile classpath for source set 'main'.
\--- commons-collections:commons-collections:3.2
default - Configuration for default artifacts.
\--- commons-collections:commons-collections:3.2
runtime - Runtime classpath for source set 'main'.
\--- commons-collections:commons-collections:3.2
testCompile - Compile classpath for source set 'test'.
+--- commons-collections:commons-collections:3.2
\--- junit:junit:4.+ -> 4.12
\--- org.hamcrest:hamcrest-core:1.3
testRuntime - Runtime classpath for source set 'test'.
+--- commons-collections:commons-collections:3.2
\--- junit:junit:4.+ -> 4.12
\--- org.hamcrest:hamcrest-core:1.3
BUILD SUCCESSFUL
Total time: 4.47 secs
This was created from a build.gradle file with only these direct dependencies:
dependencies {
compile group: 'commons-collections', name: 'commons-collections', version: '3.2'
testCompile group: 'junit', name: 'junit', version: '4.+'
}

Resources