Include transitive dependencies in forced dependency

Let’s say I have the following dependency tree

MyProj
|
|----A
|     |------B
|            |-----C
|----D

in my proj, I define

dependencies {
compile ‘A’, version 1
compile ‘D’, version 1
}

I get

MyProj
|----A (1)
|     |------B (1)
|            |-----C(1)
|----D (1)

If I recompile B with another C version

B (1.1)
|------C (1.1)

If I set

dependencies {
compile 'A', version 1
compile 'D', version 1
compile ('B' version 1.1){force=true}
}

I get

MyProj
|----A (1)
|     |------B (1.1) forced
|            |-----C(1)
|----D (1)

B only is forced, not his transitive dependencies.
How can I simply express that I want to force B and its transitive dependencies as well ?
i.e.

MyProj
|----A (1)
|     |------B (1.1) forced
|            |-----C(1.1) forced
|----D (1)

There is currently no way express this without explicitly forcing each transitive dependency. To avoid having to add all the transitive dependencies as direct dependencies you’ll probably want to configure your resolution strategy instead.

configurations.all {
  resolutionStrategy.force 'b:transitive-a:1.1', 'b:transitive-b:1.1'
}

ok, thanks Mark.
Wouldn’t it be a feature to keep in mind for future enhancement of the dependency management ?
e.g.

compile (“goup:module:version”){ force = NO / SINGLE / TRANSITIVE}

(replacing the force boolean by an enum, to include the ‘transitive’ possibility, that would force all the transitive dependencies of the forced dependency)
This mechanism would under the hood install resolutionStrategy.force SINGLE on all the found transitive dependencies
… or something like that :smile:

Can give some more context to your specific use case? I think your example is a bit overly simplistic. Forcing a newer version of a dependency is almost never necessary since conflict resolution would end up choosing that version anyway.

At work we have several sub components: A, B, C, D, E, F, G
Their relation is sth like

   A
   |
   B
 /   \
C     D
\     /
   E
   |
   F
   |
   G

Each of them has its own repository and release lifecyle (different release name, not always released at the same time)

Sometimes when developing a feature, the main modification is in C, but to make it work I also have to change something in A and B.
So I end up having three private versions: myA, myB, myC.
Before commiting each one of them, I want to perform our integration tests, which we normally launch by telling on which version of G it shall be launched.
By transitive dependency retrieval, it’s gonna take all the sub components, until A
(e.g. I tell to run with G version 1.1-snapshot, and it’s gonna look the ivy.xml to retrieve F 1.1-snap, E 1.0-snap (for instance) etc etc)

So in my case I want to use the ‘current’ G version and its dependencies, except myA, myB, myC.
I could write

dependencies {
integTest 'group:G:1.1-snapshot'
integTest ('group:C:myC'){force=true}
integTest ('group:B:myB'){force=true}
integTest ('group:A:myA'){force=true}
}

or as you suggested, I could use resolutionStrategy to specify all three versions of A,B,C
but I think it would be simpler to say

“I want to test myC version of C, and therefore ‘trust’ its declared dependencies, no matter what they are”

As you pointed out, conflict resolution will choose the newest version anyway, but that’s not necessarilly the one I want.
I want to trust the transitive version of a dependency (because I’m developing several librairies tightly coupled with each other)

I think in this case you really do want to use the resolution strategy method. This has the benefit of applying to transitive dependencies of other projects, that might also depend on A, B, or C. I think for this use case, what you really want is the ability to explicitly enforce the usage of defined set of dependencies, which is what the resolution strategy method will accomplish. In this particular case I believe it just incidentally happens to be that they are primarily all transitive dependencies of the same dependency.

Take for example you were working on project F, which required changes to C and D. You definitely wouldn’t want to use the recursive force in that case, as E might specify some dependencies that you do want conflict resolution to resolve, or for that matter, same for projects B and A.

I don’t believe we want to introduce a “recursive force” feature at this time. Doing so would have non-trivial implications. While in this case such a feature might make for a slightly more concise build script, I’m not certain it is actually the best course of action.

ok, I see your point.
I know which version I want to use anyway,so it’s not a problem of knowing what to write in the resolutionStrategy block. Thanks for your insight

Hi Mark, I am going through

https://docs.gradle.org/current/dsl/org.gradle.api.artifacts.ResolutionStrategy.html#org.gradle.api.artifacts.ResolutionStrategy:force(java.lang.Object[])

and it says "Allows forcing certain versions of dependencies, including transitive dependencies. "

So, I am assuming the transitives should be forced too. Is that not or am I misreading it?

Regards,
Mohan

What it means is that the module will be forced even if it is a transitive (non-direct) dependency. What it will NOT do is also force its transitive dependencies, which is what Francois was talking about above. Basically, only the single module is forced, we do not recursively set force = true on all the transitive dependencies as well.

Thanks Mark. Got it.

Regards,
Mohan