Gradle Module Metadata - Attributes - How do I define minimum Integer value to select the correct variant in a .module?

My Question

I have defined a custom variant attribute in a Gradle Plugin I am developing which I will use to select the correct variant in the Gradle Module Metadata file .module for my library . I have it defined as an Integer and it is working correctly to match an exact value to section the current variant. However I would like to define a minimum value instead of requiring an exact value.

What I have done

My Gradle Plugin

Define a custom attribute called custom.compileSdkVersion:

final myAttributeCompileSdkVersion = Attribute.of('custom.compileSdkVersion', Integer)
dependencies.attributesSchema {
    attribute(myAttributeCompileSdkVersion)
}
configurations.all {
    attributes {
        attribute(myAttributeCompileSdkVersion, 31)
    }
}

My Library’s .module file

Not the full file, just the relevant parts.

{
          "formatVersion": "1.1",
          "component": {
            "group": "com.test.local",
            "module": "liba",
            "version": "1.1.0"
          },
          "variants": [
            {
              "name": "custom.compileSdkVersion_require_min_30",
              "attributes": {
                "org.gradle.category": "library",
                "org.gradle.dependency.bundling": "external",
                "org.gradle.libraryelements": "aar",
                "org.gradle.usage": "java-runtime",
                "custom.compileSdkVersion": 30
              }
            }
        }

App Project’s build.gradle

dependencies {
  implementation 'com.test.local:liba:1.1.0'
}

Results

If I run ./gradlew -q app:dependencyInsight --dependency liba --configuration debugCompileClasspath I get the following error:

com.test.local:liba:1.1.0 FAILED
   Failures:
      - Could not resolve com.test.local:liba:1.1.0.
          - No matching variant of com.test.local:liba:1.1.0 was found. The consumer was configured to find an API of a component, preferably optimized for Android, as well as attribute 'custom.compileSdkVersion' with value '31', attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'debug' but:
              - Variant 'custom.compileSdkVersion_require_min_30' capability com.test.local:liba:1.1.0 declares a runtime of a component:
                  - Incompatible because this component declares a component, as well as attribute 'custom.compileSdkVersion' with value '30' and the consumer needed a component, as well as attribute 'custom.compileSdkVersion' with value '31'
                  - Other compatible attributes:
                      - Doesn't say anything about com.android.build.api.attributes.BuildTypeAttr (required 'debug')
                      - Doesn't say anything about its target Java environment (preferred optimized for Android)

com.test.local:liba:1.1.0 FAILED
\--- debugCompileClasspath

However if I change myAttributeCompileSdkVersion to 30 it correctly resolves com.test.local:liba:1.1.0., but I want it to also work when myAttributeCompileSdkVersion is 31 or higher. I wasn’t able to find an answer to this anyone in the Gradle doc or anyone else asking about this on the forums here.

Why this important

This is needed to pick the correct library based on the Android compileSdkVersion selected by the app’s project. See more details here: Google Issue Tracker

Research

How org.gradle.plugin.api-version is defined

I found that this defines the attribute itself a class like this class GradlePluginApiVersion extends Named and also defines a class GradlePluginVariantsSupport to add a AttributeMatchingStrategy.This seems like the way to do it however.

This sentence in the docs makes it sound very limiting and isn’t correct since Integer DOES work (only as exact values though). I didn’t know what Named was, should probably say org.gradle.api.Named and link to something explaining it as well.

Currently, only attribute types of String , or anything extending Named is supported. Attributes must be declared in the attribute schema found on the dependencies handler:

Conclusion and probably the solution

Anyway the Attribute compatibility rules and Attribute disambiguation rules sections at the bottom explain from a high level what these are and how it can let you define a more flexible compare instead of just an exact value. It was just easy to overlook, and doesn’t provide any examples, even pointing to GradlePluginVariantsSupport would be a very helpful link in these docs!

I wasn’t able to find an answer to this anyone in the Gradle doc

It is mentioned in the docs as you found out below
There is even an example for the compatibility rule in the docs which you didn’t find yet it seems, you should probably open an issue or PR to improve the docs so that the high-level section links to the actual example. :slight_smile:

This sentence in the docs makes it sound very limiting and isn’t correct since Integer DOES work

I agree, org.gradle.plugin.api-version is one example for that, org.gradle.jvm.version is another one and both work with the <= logic you want. You should open another issue for that to update the sentence with the currently valid types or remove it if all types are allowed now.

I didn’t know what Named was, should probably say org.gradle.api.Named and link to something explaining it as well.

Also here I agree and suggest you add another issue to have it linked to the JavaDoc.

It was just easy to overlook, and doesn’t provide any examples, even pointing to GradlePluginVariantsSupport would be a very helpful link in these docs!

If you search for AttributeCompatibilityRule, you find the instrumented-jars example which at least shows how to define attribute compatibility.
But I agree that there should be more example and also one how to use disambiguation rule.
I again suggest to open another issue for it. :slight_smile:

Actually for your use-case you can probably just do it like it is done for org.gradle.jvm.version where you don’t need a full attribute compatibility rule and attribute disambiguation rule implementation, but can just use ordered and pickLast as you can find in org.gradle.api.internal.artifacts.JavaEcosystemSupport#configureTargetPlatform.

1 Like

I agree, org.gradle.plugin.api-version is one example for that, org.gradle.jvm.version is another one and both work with the <= logic you want. You should open another issue for that to update the sentence with the currently valid types or remove it if all types are allowed now.

I made a few small changes to address the Integer type and to include the namespace with Named to help with that. Didn’t see a good way to also bring up org.gradle.plugin.api-version and org.gradle.jvm.version without putting to much detail where probably shouldn’t be. Here is the PR sumbitted:

If you search for AttributeCompatibilityRule , you find the instrumented-jars example which at least shows how to define attribute compatibility.
But I agree that there should be more example and also one how to use disambiguation rule.
I again suggest to open another issue for it.

Thanks, there is a lot of detail there and looks like everything I need to understand.

Defining custom attributes for variants is something that is pretty advanced and most developers are not going to need to create their own. Overall the page contains what is needed to utilize the feature, I think I was just getting fatigued trying to understand and weight options. I went back and read whole page and things clicked.

Declaring custom attributes section in the docs has this good callout which I also want to highlight that using custom attributes on publicly available libraries means you have a responsibility to keep things compatible.

1 Like