Sharing dependencies across subprojects

Let’s see if I can explain this in a way that makes sense. I have a project with three subprojects, A, B, and C. A is a library used by B and C. A has some classes that require third party libraries. A1 depends on Red, Yellow, Blue, A2 depends on Green, etc.

So in A, I can say

compileOnly("Red")
compileOnly("Yellow")
compileOnly("Blue")
compileOnly("Green")

And then in B and C I can say

implementation("Red")
implementation("Yellow")
implementation("Blue")
implementation("Green")

etc. But all that repetition is tedious and of course Red, Yellow, and Blue are more complex strings with version numbers.

It seems like I should be able to declare Red, Yellow, and Blue somewhere and then just reuse that.

This StackOverflow post uses the Groovy DSL and suggests that you can pass a list when you’re constructing the dependency:

implementation(referenceToListOfString)

But the Kotlin DSL doesn’t accept a list of strings there. I assume I’m overlooking something, or failing to grok some aspect of the implementation closure or something. Is it obvious to someone else?

Actually, I think I worked this out. I created common, application, and library convention plugins in buildSrc and configured the dependencies there. I still wind up with two copies, one as compileOnly and one as implementation but maybe that’s unavoidable. Two is certainly managable where three seemed quite awkward.

Why do you have them as compileOnly in A and then as implementation in B and C?
This sounds wrong to start with.

compileOnly is for dependencies you only need at compilation time but not at runtime which does not seem to be your use-case.

B and C should only have implementation dependencies if they actually use classes from those dependencies for their own implementation.

If A uses these libraries for its implementation, they should be implementation.
If A uses these libraries for its API (return types, parameter types, super types, … [not annotations, those are compileOnly]), they should be api.

If the point is, that you want “optional” dependencies, depending on which features the consumer wants to use, you should instead create feature variants. Then a consumer can say “I want A-for-feature-1” and gets the necessary dependencies and / or “I want A-for-feature-2” and gets the necessary dependencies.

If they use Gradle that is. For Maven consumers those would then be “optional” dependencies.

Have a look at Optional dependencies are not optional for reference.

(Keep in mind Please revert the deprecation to declare additional feature variants on the `main` source set · Issue #29455 · gradle/gradle · GitHub if you would choose to follow the approach of having one code artifact with different sets of dependencies, like shown in the blog post is currently deprecated and needs a bit more legwork)