Hi folks. I'm sure this has been asked before (may...
# community-support
c
Hi folks. I'm sure this has been asked before (maybe even by me), question about this kind of migration. I have an Android project with dependencies like this:
MyApp
depends on
depA
, which depends on an old version of
depB
:
Copy code
MyApp -> depA -> depB
I want to break the dependency on
depA
and instead depend directly on a new version of
depB
. However I need to keep the previous arrangement, to support a gradual rollout of the change:
Copy code
MyApp
  -> depB
  -> depA -> depB
From talking to Claude I am under the impression this is basically impossible, since there will be conflicts on the classpath. Yet this seems like it may be a common requirement for projects, so I'm wondering how other people handle this. Thanks for your time.
a
p
Gradle will include the higher version, unless you specify it explicitly. But regarding compatibility, it really depends on the library and the versions/(mayor) changes.
v
It is even absolutely normal to have this setup in every project except for minimal ones. Every time you use classes from depB, you should declare an explicit dependency on it, even if it is already available transitively through depA. What happens if there are multiple versions of depB requested depends on your setup, you can configure it to fail the build so that you have to manually resolve the conflict, but by default the newer version will end up on the class path as Philip said. Of course all usages of depB within depA that you trigger have to be compatible with the version of depB that ends up on the classpath. That's just the normal dependency hell dance.
c
I realized I framed the problem wrong. The version used is not exactly my main concern. I don't want to upgrade the transitive dependency (in fact, the opposite). I'm trying to break a dependency on a vendor, and
depB
is an open source project I want to build myself. I don't want to change anything about the vendor's transitive dependency. So what I have is this:
Copy code
MyApp -> vendorDependency -> vendorBuildOfOpenSourceProject
I am trying to break
vendorDependency
and rely on the (upstream) open source project, so that the end result looks like
Copy code
MyApp -> myBuildOfOpenSourceProject
The artifacts
vendorBuildOfOpenSourceProject
and
myBuildOfOpenSourceProject
will occupy the same package namespace (
com.example.etc
). I don't know if the vendor build is a fork or a just a very old build. However, I need a gradual rollout, so I need to support the vendor fork and the upstream build at the same time:
Copy code
MyApp
 -> vendorDependency -> vendorBuildOfOpenSourceProject
 -> myBuildOfOpenSourceProject
When I have tried this previously, I encountered conflicts on the classpath iirc. I'm wondering how I can manage this situation.
Appreciate you guys taking the time to reply, thank you
v
You build it under different coordinates, so have the same packages in different coordinates? That is usually not really a good idea. It would be better if it is the same coordinates and you just either have a newer version or use a strict version or similar. To have it clean with different coordinates, you should probably have in the gradle module metadata the same capability to get a conflict even if the coordinates are different and then have a capability conflict resolution that selects the intended one. Also, if you do not use classes from that dependency yourself, you should not declare a dependency but when using the same coordinates just have a version constraint with the version, or when using different coordinates have a substitution rule so that it takes your other coordinates when the original coordinates are requested. If you do use classes from that dependency and have different coordintes, a quick-and-dirty solution instead of using the capability conflict, would be to exclude the transitive dependency and declare yours. But either way, the version you built needs to be compatible so that the
vendorDependency
can work with those classes.
c
You build it under different coordinates, so have the same packages in different coordinates?
Yes, unfortunately I am not able to control the vendor build. The open source package in question is a combination of Java and JNI bridge into C++, and the native layer calls back into Java packages using some hardcoded names. We investigated trying to build to another package target but I find that too risky. Just so I understand correctly, when you say coordinates you are talking about module version identifier correct? (group, name, version) From what you describe, it sounds like I could resolve the coordinates at build time, but not runtime?
v
when you say coordinates you are talking about module version identifier correct? (group, name, version)
Yes
it sounds like I could resolve the coordinates at build time, but not runtime?
Coordinates are always resolved at build time, so I might not get what you mean
c
Coordinates are always resolved at build time
Er, yes. I think that's what I mean, there is no way for me to "resolve the dependency at runtime"
v
What do you mean by that?
c
My hypothetical ideal scenario would be to have both artifacts at different coordinates, both available at runtime, which I can toggle between using a runtime feature toggle. I take it that this is basically impossible, or at least deeply risky/impractical. I'm building for Android, so if I have a regression that requires a new build, it takes time for a fix to get approved / deployed / propagated. I'm trying to avoid that if I can.
v
If you want to have both at runtime, you need different class loaders.
But that is no longer a Gradle topic then and thus off-topic here.
c
Thanks for the help
j
it can be done, if in your build of the OSS project you relocate the classes to a different package, then in your app, write an interface with 2 impls, one that uses the vendor classes and one that uses your build, with the classes relocated. you can look at the #C07GJEMUZDH shadow plugin for how to relocate classes
c
I'm curious about that approach but the problem is that the package includes C++ code which references the original package name
j
oh, well that is bad news
v
As he builds it himself he could also just change the packages before building. But if you read the thread he has JNI code that accesses those classes and I doubt shadow plugin can relocate that.
j
yeah, I missed that part
c
All good, thank you for trying to help