Home > database >  How does gradle choose actually libraries ending with "-jvm"?
How does gradle choose actually libraries ending with "-jvm"?

Time:10-02

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 list of kotest-runner-junit5-jvm dependencies in Maven Central, with a file 'kotest-runner-junit5-5.4.2.module' highlighted

    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 a available-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 that testImplementation 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.

  • Related