Gradle transient dependencies - gradle

I have a project "lib" with declares dependency on third party library (lets say, redis).
dependencies{
implementation group: 'redis.clients', name: 'jedis', version: '2.9.0'
}
I have another project, "application", which is declares dependency on "lib" project in very similar way.
The problem: unless I adding redis dependency to the "application" project as well, it fails at runtime due to missing redis dependency (despite that it itself doesn't make any direct use of redis).
I want to declare "redis" dependency inside "lib" project in such way, so "lib" will be already "bundled" with "redis" inside, so everyone using "lib" will have to declare only "lib" dependency.
How to do that?

Assuming you apply the java or java-library plugins, you should not have to do anything.
A dependency added to the implementation scope in Gradle is visible to consumers of that project for their runtime classpath. It is however not visible to the compile classpath.
In order to understand better what's happening, you can check the different classpath by running ./gradlew <project>:dependencies --configuration runtimeClasspath on both projects and see what is the output, of course replacing <project> with the project name or leaving empty for the root project.

Related

Which dependencies are added to a Gradle project which uses a 'project lib dependency' and is this configurable?

Gradle project lib dependencies are a means of defining a project as requiring, as one of its dependencies, the output from another project.
The documentation states:
A “lib” dependency is a special form of an execution dependency. It
causes the other project to be built first and adds the jar with the
classes of the other project to the classpath. It also adds the
dependencies of the other project to the classpath.
However, which dependencies of the project are added? Is it the compile, implementation or runtime dependencies, for instance? Is this configurable? i.e. how would I configure a project to require the output of another project and the dependencies from an arbitrary configuration of that other project?
For instance, in my root project, I can define project1 to depend on project2:
project(":project1") {
dependencies {
implementation project(':project2')
}
}
How do I add the dependencies of an arbitrary configuration of :project2 (let's say it's called myConfiguration) to project1?
The answer is contained within the DSL documentation for Dependencyhandler.
By default, when you declare dependency to projectA, you actually
declare dependency to the 'default' configuration of the projectA. If
you need to depend on a specific configuration of projectA, use map
notation for projects:
configurationName project(path: ':projectA', configuration: 'someOtherConfiguration')

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.

How does Gradle handle redundant dependencies

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.

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.

Resources