Converting a transitive dependency into a compileOnly dependency - gradle

I am trying to use OSGI to allow me to use two different versions of a transitive dependency. The plan is that one version (the newer version) will be hidden away inside an OSGI bundle, the other will be on the runtime classpath as normal.
I have built the bundle jar using Gradle (with the Groovy DSL), but the problem is its associated runtime dependencies are wrong - it brings along the newer version, which is supposed to be hidden inside the bundle. This is still true when I do, in the build.gradle file:
compileOnly deps.diffx
runtimeOnly(deps.diffx) {
exclude group: 'com.propensive', module: 'magnolia_' + versions.scala_v
}
If I examine the dependencies with the Gradle dependencies task, it shows that magnolia is excluded from the runtimeOnly configuration, as expected - but is not excluded from the runtimeClasspath configuration.
If I then use ./gradlew dependencyInsight --dependency magnolia_2.12 --configuration runtime to try to find out where this dependency is coming from, it tells me that the newer version is coming from runtimeClasspath depending on diffx, and this is selected via conflict resolution. Well thanks - I already knew that. The question is, why is my exclusion not being applied to the derived configuration?
Basically I want to do the opposite of this question.
I also tried constraint versions, but they exhibited the same problem:
compileOnly deps.diffx
runtimeOnly(deps.diffx) {
constraints {
implementation('com.propensive:magnolia_' + versions.scala_v + ':0.10.0') {
because 'this version is required by our other dependencies'
}
}
}

From the Gradle documentation:
On the upside, Gradle’s exclude handling is, in contrast to Maven, taking the whole dependency graph into account. So if there are multiple dependencies on a library, excludes are only exercised if all dependencies agree on them. For example, if we add opencsv as another dependency to our project above, which also depends on commons-beanutils, commons-collection is no longer excluded as opencsv itself does not exclude it.
So instead, we can use a dependency resolve rule:
def magnoliaVersion = '0.10.0'
configurations.runtimeClasspath {
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
if (details.requested.group == 'com.propensive' && details.requested.name.startsWith("magnolia_") && details.requested.version != magnoliaVersion) {
details.useVersion magnoliaVersion
details.because 'this version is required by our other dependencies'
}
}
}
and then change the dependency back to a simple, single implementation one:
implementation deps.diffx
Unfortunately, as the documentation notes, this resolve rule isn't published, so it has to be applied again in any dependent Gradle modules.

Related

How to excplude module cxf-rt-transports-http

I have the following dependency in build.gradle:
compile ('org.apache.cxf:cxf-bundle:2.4.2') {
exclude module: 'log4j-over-slf4j'
}
I would like to also exclude the module that contains transports-http because of a conflict between libraries (using HTTPConduit). I tried to exclude it, but I don't think I have the right module name
The dependencies task will give you a dependency report where you can see what transitive dependencies are being pulled in.
./gradlew dependencies --configuration compileClasspath
If you were to examine the output of that task, you'll see that org.apache.cxf:cxf-bundle does not appear to bring in cxf-rt-transports-http. So you'll need to examine your project to figure out where the duplicate dependency is.
Gradle also offers a means to handle version conflicts as well: https://docs.gradle.org/current/userguide/viewing_debugging_dependencies.html#sec:resolving-version-conflict

Exclude a class depedency in compile, but include in testCompile for gradle

I'm having an issue where I have an old version of library Foo as a transitive dependency in one of my included libs. I want to use a newer version of library Foo in my testCompile, but it's causing a conflict when I run tests in intelliJ, as intelliJ is defaulting to using the older Foo lib.
I've tried excluding Foo, and then explicitly including it in testCompile, but it seems the exclude overrides the include in testCompile.
I'm currently using the below to force a resolution, but I would prefer I don't force compile dependencies to a specific version.
configurations.all {
resolutionStrategy {
force 'org.foo:bar:1.0'
}
}
You might consider configuring idea.module.scopes.TEST like so:
idea {
module {
scopes.TEST.minus += ['org.foo:bar:0.9-oldversion']
}
}
and then running gradle cleanIdea idea. You could also consider modifying the testClasses configuration such that the old version is excluded.

How do I use only file based dependencies in gradle instead of specifying groupId:artifactId:versionId

I have the following entry in my gradle file
dependencies {
compile 'org.A:A:1.0'
}
which downloads 'org.B:B:1.0' because that's it's dependency.(not mentioned explicitly in gradle)
What I want to use in my project is A* and B* which are shadows(changed namespace) of A and B respectively.
Now, I have specified the dependency for A* as
dependencies{
compile file('libs/A*.jar')
}
But, this one still downloads 'org.B:B:1.0'
How do I wire the gradle to use file('libs/B*.jar')?
The first solution that comes to mind is to exclude the transitive dependency of compile 'org.A:A:1.0'
This works like this:
dependencies {
compile('org.A:A:1.0') {
exclude 'org.B:B:1.0'
}
}
Have a look at the Gradle User Guide for more details on that subject.
As mentioned in the comments, file dependencies can't have transitive dependencies. So A*.jar either has the contents of the B.jar rolled into it somehow, or you have B.jar on the build path somewhere else.
File dependencies are generally a tool of last resort, they do not participate in conflict resolution; you need a dependency repository like maven or ivy for that.

Gradle trump providedCompile's exclusion?

I've got a project that I want to use providedCompile with to avoid pulling it's libs. However, that prevents me from pulling in another project's libs that I do need. The docs say: If you don't want this transitive behavior, simply declare your provided dependencies, but it doesn't give an example of how to do this.
Here's basically what my dependencies look like:
dependencies {
compile(project(':common'))
providedCompile(project(':projA')) // <-- also depends on :common
}
My war file correctly excludes all of transitive libs from projA, but I need to trump that for the common.jar and I can't figure out how to make that happen. But the docs seem to indicate it's possible...
Edit: Here's a hacky configuration that seems to work. The combination of lines for "projA" gives me projA.jar as a dependency, but not its children. And since "common" is a compile dependency, but "projA" is only considered provided at runtime, I still get the common.jar due to the compile time dependency. I'm not sure it's supposed to work this way, but it generates the war I need.
dependencies {
compile(project(':projA')) { transitive = false }
providedRuntime(project(':projA')) { transitive = false }
compile(project(':common'))
}
If you don't want this transitive behavior, simply declare your provided dependencies
This means that if you don't want all dependencies of projA to be declared provided you need to list them as provided one by one.
From the Gradle 1.8 Userguide '26.4. Dependency management'
The War plugin adds two dependency configurations: providedCompile and
providedRuntime. Those configurations have the same scope as the
respective compile and runtime configurations, except that they are
not added to the WAR archive. It is important to note that those
provided configurations work transitively.
Let's say you add commons-httpclient:commons-httpclient:3.0 to any of
the provided configurations. This dependency has a dependency on
commons-codec. This means neither httpclient nor commons-codec is
added to your WAR, even if commons-codec were an explicit dependency
of your compile configuration. If you don't want this transitive
behavior, simply declare your provided dependencies like
commons-httpclient:commons-httpclient:3.0#jar.
There is another better solution that seems to be working. With transitive=false you exclude all the transitive dependencies that are then missing during compilation time and you have to declare all of them manually (again as provided) which is pain if they also include your desired library.
So for the providedCompile statement exclude not all transitive dependencies but only the one you want to include in war by separate compile statement.
Real example where I need the commons-codec included in the war file but that one is placed also in the keycloak-services and keycloak-model-jpa:
providedCompile ("org.keycloak:keycloak-model-jpa:6.0.1") {
exclude group: 'commons-codec', module: 'commons-codec'
}
providedCompile ("org.keycloak:keycloak-services:6.0.1") {
exclude group: 'commons-codec', module: 'commons-codec'
}
providedCompile "org.keycloak:keycloak-core:6.0.1"
providedCompile "org.keycloak:keycloak-server-spi:6.0.1"
providedCompile "org.keycloak:keycloak-server-spi-private:6.0.1"
compile "commons-codec:commons-codec:1.10"

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