I'm migrating from Gradle to Bazel.
I had in my gradle build a testImplementation
dependency to io.kotest:kotest-runner-junit5:5.4.2
. It works perfectly.
I add the same dependency to my Bazel config files (WORKSPACE and BUILD), but I get compilation errors, as if the library doesn't exist.
I go and check if Bazel doesn't bring transitive dependencies, but it does.
I check the POM of the library and it turns out it has no dependencies.
I see in maven there's another called io.kotest:kotest-runner-junit5-jvm:5.4.2
.
I use that one instead. Voilá, it works!
But why? how is gradle picking the -jvm
artifact instead?
CodePudding user response:
There are 3 parts to your question
- How does Gradle variants are available?
- How does Gradle find the variants?
- How does Gradle figure out how to pick the right variant?
The short answer is that
- Gradle publishes an additional
Because it's JSON, we can look inside. There's some metadata
{ "formatVersion": "1.1", "component": { "group": "io.kotest", "module": "kotest-runner-junit5", "version": "5.4.2", "attributes": { "org.gradle.status": "release" } }, ...
There's also a
variants
array. One of the variants is the-jvm
variant, as well as aavailable-at.url
, which is a relative path linking to the available variants within the Maven repository.... "variants": [ ... { "name": "jvmRuntimeElements-published", "attributes": { "org.gradle.category": "library", "org.gradle.libraryelements": "jar", "org.gradle.usage": "java-runtime", "org.jetbrains.kotlin.platform.type": "jvm" }, "available-at": { "url": "../../kotest-runner-junit5-jvm/5.4.2/kotest-runner-junit5-jvm-5.4.2.module", "group": "io.kotest", "module": "kotest-runner-junit5-jvm", "version": "5.4.2" } } ... ] }
That's how Gradle discovers the available variants.
Variant selection: Attribute matching
There's actually several variants in the module, and there would be even more if more Kotlin Multiplatform targets were enabled, so the final question "how does Gradle figure out what variant is needed?"
The answer comes from the
"attributes"
that are associated with the variant. They're just key-value strings that Gradle uses to match what's required, to what's available.https://docs.gradle.org/current/userguide/variant_attributes.html#attribute_matching
The attributes might say
I want a Java 8 JAR for org.company:some-artifact:1.0.0
or
I want a Kotlin Native 1.7.0 source files for io.kotest:something:2.0.0
They're just key-value strings, so they can really be anything. I've created attributes for sharing TypeScript files, or JaCoCo XML report files.
Why do we never see these attributes when we write Gradle files?
When you add a dependency in Gradle
// build.gradle.kts plugins { kotlin("jvm") version "1.7.20" } dependencies { testImplementation("io.kotest:kotest-runner-junit5:5.4.2") }
There's no attributes. So how does Gradle know to select the
-jvm
variant?Gradle sees you've added a dependency using
testImplementation
, which is a Configuration.(Aside: I think the name 'Configuration' is confusing. It's not configuration for how the project behaves. I like to think of it more like how a group of naval warships might have a 'configuration' for battle, or a 'configuration' for loading supplies. It's more about the 'shape', and it's not about controlling Gradle properties or actions.)
When Configurations are defined, they're also tagged with attributes, which Gradle will use to play matchmaker between the request for "kotest-runner-junit5" and what it discovers in the registered repositories
In the case of
testImplementation("io.kotest:kotest-runner-junit5:5.4.2")
, Gradle can see thattestImplementation
has Attributes that say "I need a JVM variant", and it can use that to find a matching dependency using the module metadata in Maven Central.