How does Gradle handle redundant dependencies - gradle

If I have two dependencies in my build.gradle file that refer to group artifacts and both of these groups contain references to the same jar files, how does Gradle handle them? Does Gradle recognize the version difference between the individual jar files that make up the group and only select the one with the newest version when a compile is made or does it pick the one that is listed last in the build.gradle file? Example:
dependencies {
compile group: 'some-sdk-1', name: 'sdk1', version: '2.5'
compile group: 'some-sdk-2', name: 'sdk2', version: '1.0'
}
In this example, some-sdk-1 might contain a jar called lib1-1.0.jar
In some-sdk-2, the same library is present but has a different version. For example lib1-2.0.jar
Which jar file is used?

It depends on how the jar file is put inside the library (sdk).
You should manage the jar files as transitive dependencies.
It means that inside the pom file related to the sdk you have a dependency of the jar file.
In this case you can check the doc:
Gradle offers the following conflict resolution strategies:
Newest: The newest version of the dependency is used. This is Gradle’s default strategy, and is often an appropriate choice as long as versions are backwards-compatible.
Fail: A version conflict results in a build failure. This strategy requires all version conflicts to be resolved explicitly in the build script. See ResolutionStrategy for details on how to explicitly choose a particular version.
Instead if you are just putting the jar inside the sdk without using a transitive dependency (just the jar file inside):
If you don’t use transitive dependency management, version conflicts are undetected and the often accidental order of the classpath will determine what version of a dependency will win.

Related

spring boot plugin and gradle dependency implementation

We have been using gradle spring boot plugin version 1.5.14 and gradle 4.10.3 for our builds. After an upgrade of gradle to 6.2.2, we've also changed dependency-definition from compile group to implementation group i.e.:
compile group: 'org.springframework.boot', name: 'spring-boot-starter-integration'
to
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-integration'.
The fat jar created via gradle assemble does to my surprise not contain the required libs under BOOT-INF/lib anymore? If I replace "implementation" with "compile" it works again as expected.
Is there something which needs to be configured so that spring-boot-plugin adds them? Or do I need to upgrade the project to spring boot 2 beforehand?
Implementation will bring in the dependency only for that module it is declared in.
Compile will allow a module that depends on the module to use the dependency as well.
Say you have Module A depending on Module B.
Module A and B both need the dependency:
com.font:font1:1.1.1
If B uses:
implementation 'com.font:font1:1.1.1'
A will not see font1, and will need to bring it into its own build.gradle file as well.
compile 'com.font:font1:1.1.1'
will make it available to the entire classpath (which should be avoided anyway, and the new equivalent is for libraries which uses 'api' instead of 'compile').
Without seeing your project directory, I'm assuming that some dependency is not being pulled into a place where it used to be grabbed from a lower dependency in the hierarchy. You should be able to find the missing dependencies easily by changing compile to implementation one at a time, unless you have a huge multi-module project.

when downloading a dependency jar, how to make its dependencies get downloaded

I have built two jars and put them in Artifactory. One of the jars depends on the other (the dependency is in its build.gradle file). When I download the main jar as a dependency of my main app, the dependent jar is not downloaded. The only way I can get both is to put two compile statements in the build.gradle. How do I cause the dependent jar to also get downloaded?
The main jar file is user-cache.jar and it depends on blue-redis.jar. The build.gradle in the app that uses my main jar uses this compile statement:
compile(group: 'etd.user-cache', name: 'user-cache', version: '1.0.2', ext: '12.SNAPSHOT.jar')
The build.gradle for user-cache has this in it:
compile(group: 'etd.blue-redis', name: 'blue-redis', version: '1.0.1', ext: '4.SNAPSHOT.jar')
When I build my app, it only gets user-cache.jar. That makes it necessary to put both compile statements in my app's build.jar
What should I do to cause the blue-redis.jar to also be downloaded without needing its compile statement?
I assume that you are using a maven repository in Artifactory. When gradle is doing dependency resolution it will attempt to download a POM file and check for transitive dependencies as well as parent poms which may list additional dependencies.
To get the desired behavior, when you publish the main jar to Artifactory you need to include in its POM file a dependency on the other JAR.

Gradle - don't specify version in included dependencies names

My application gets packaged as ear and I have used earlib and deploy configuration. However for all those dependencies version gets mentioned in the jar names.
For example, if I mention dependencies as below,
earlib 'com.xyz:abc:1.0.1'
In generated ear I can see jar name as abc-1.0.1.jar but I want to get it included simply as abc.jar.
Declare a dependency without a version
Gradle lets you declare a dependency without a version but you have to define a dependency constraint, which basically is the definition of your dependency version. This is commonly used in large projects:
dependencies {
implementation 'org.springframework:spring-web'
}
dependencies {
constraints {
implementation 'org.springframework:spring-web:5.0.2.RELEASE'
}
}
Declare a dynamic version
Another option is to declare a dynamic version by using the plus operator. This allows you to use the latest relase of a dependency while you pack your application. Doing so is potentially dangerous since its bears the risk of breaking the application:
apply plugin: 'java-library'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework:spring-web:5.+'
}
Declaring a file dependency
If you don't want to rely on a binary repository at all but provide dependencies yourself, you can declare a file dependency, from the directories ant, libs and tools. This allows you to name and version dependencies as you like but you have to maintain them yourself:
configurations {
antContrib
externalLibs
deploymentTools
}
dependencies {
antContrib files('ant/antcontrib.jar')
externalLibs files('libs/commons-lang.jar', 'libs/log4j.jar')
deploymentTools fileTree(dir: 'tools', include: '*.exe')
}
Note I recommend against removing the versions as they are important diagnostic information when the application doesn't work.
The ear task is an instance of the Ear task type, which in turn is basically a specialised form of the standard Zip task type. All archiving tasks allow you to rename files as they are packed.
For example, the following might work:
ear {
rename '(.+)-[^-].+(\\.jar)', '$1$2'
lib {
rename '(.+)-[^-].+(\\.jar)', '$1$2'
}
}
I strongly recommend that you check out the new user manual chapter on Working with files for more information about copying and archiving files. Hopefully I'll remember to update this answer with the non-release-candidate link once Gradle 4.7 is out.
Also, if you have any feedback on that chapter let me know.
EDIT Based on OP's feedback, I discovered that the Ear task uses a child copy specification for the JARs in the earlib configuration. Child specifications are independent of both the main one and other child specs, so the main rename() doesn't apply to the earlib files. That's why we add a rename() via the lib {} block.

Determine source of transitive dependency

I have a project in which I am using sl4j with log4j. I recently added a few new dependencies to my project, and one of these new dependencies that I added is including a transitive dependency to logback-classic, which includes another binding for sj4j.
I want to get rid of logback, but I have no clue which of my direct dependencies added the transitive dependency so that I can exclude it.
In maven I know how to get the entire graph of dependencies to determine which is the source of a transitive dependency, but I have no clue of how to do this with gradle.
Does anyone knows how to get the source dependency of a transitive dependency with gradle?
To show the whole dependency tree for each class path, use:
> gradle dependencies
If you are only interested in a particular class path, use (say):
> gradle dependencies --configuration compile
Use the -p option to run on a sub-project.
To show who pulls in a particular dependency onto a particular class path, and how any version conflicts were resolved, use (say):
> gradle dependencyInsight --dependency logback --configuration compile
Note that you can also exclude a dependency from a whole class path (or multiple). Usually this is more reliable than excluding a particular transitive dependency. For example:
configurations.all*.exclude module: "logback-classic"
An updated answer:
I used this code to resolve a dependency issue between logback and log4j:
configurations.all {
resolutionStrategy.dependencySubstitution {
substitute module('org.apache.logging.log4j:log4j-slf4j-impl') using module ('ch.qos.logback:logback-classic:1.2.3')
}
}
This solution finds any dependencies on log4j-slf4j-impl and instructs it to choose the one from logback (this is a spring app). This solution was surprisingly difficult to track down, but likely very useful in many situations.
Here's the gradle documentation on handling conflicting candidates.

Resolve Gradle Transitive Dependency Conflict with file system libs (ie. not maven, ivy)

We have a java project with dependencies that looks something like this.
A -> B -> httpcore-4.0.1
\
C -> httpcore-4.1.3
So there is transitive dependency conflict in A. The gradle docs say that the resolution policy is to select the newest (http://gradle.org/docs/current/userguide/dependency_management.html). However, we get compile errors due to function signature differences so the latest doesn't seem to be selected. I've seen various exclude methods but not sure how they apply when we are using a file system based dependency lib (not maven or ivy). Eclipse seems to resolve the problem okay and compile but gradle barfs. I've tried various forms of:
configurations {
all*.exclude group:'org.apache', name: 'httpcore', version:'4.0.1'
all*.exclude group:'org.apache.httpcomponents', name: 'httpcore', version:'4.0.1'
}
What am I missing here?
I'm using Gradle 1.0-milestone-8a
It's just not done. See http://forums.gradle.org/gradle/topics/resolve_gradle_transitive_dependency_conflict_with_file_system_libs_ie_not_maven_ivy
You have to use a local or remote repos.

Resources