Consider the following simple build (settings.gradle.kts
):
dependencyResolutionManagement {
repositories {
mavenCentral()
}
}
include("main-module")
include("specific-test-module")
The main module is as simple as:
plugins {
id("java")
}
dependencies {
implementation("org.slf4j:slf4j-api:2.0.9")
}
My goal is to inherit implementation
dependencies from main-module
in testImplementation
scope in specific-tests-module
. The test module is so specific, that I have created a separate Gradle sub-project for it. Now I am trying to achieve my goal with extendsFrom
on configuration, but the problem is that extendsFrom
stops working as soon as I try to use it outside of a single Gradle sub-project.
While adding a new scope in main-module/build.gradle.kts
works as expected:
configurations.create("someNewScope") {
extendsFrom(configurations.implementation.get()) // <--- 'someNewScope' will inherit 'slf4j-api'
}
an attempt to inherit scope in another sub-project does not make any effect:
configurations.create("someScope") {
extendsFrom(project(":main-module").configurations.implementation.get())
}
Seems like an attempt to read configuration of another sub-project happens too early, when dependencies of another sub-project are not yet configured. How can I make scopes inheritance to work between different Gradle's sub-projects?
The idiomatic and the only recommended approach to share something between Gradle projects is to use "Variants". Here is the corresponding documentation section: Producing and Consuming Variants of Libraries > Sharing outputs between projects.
It explicitly says:
and provides the following anti-pattern example:
Very similar to what I've tried to do initially and it was wrong.
In my case it was enough to follow the Simple sharing of artifacts between projects section of the documentation and expose dependencies through "consumable" configurations like this:
After that I was able to add dependency on a project and inherit its exposed configurations as follows:
The reason why it's necessary to create additional configuration which from the first sight just
extendsFrom
already existing configuration is because you need "consumable" configuration to make it visible to other projects. Here "consumable" configuration is a variant of how other Gradle projects can consume the project when specifying it independency {}
block.In my solution I:
testImplementation(project(":main-module"))
(to add dependency onsrc/main
ofmain-module
)main-module
by explicitly specifying variant name, for exampletestImplementation(project(mapOf("path" to ":main-module", "configuration" to "exportedTestImplementation")))
(to inherit dependencies of:main-module
through "consumable" configuration)Additional references: