Can I configure the variant matching algorithm so ...
# dependency-management
m
Can I configure the variant matching algorithm so that any variant missing a given attribute gets rejected? I’m under the impression that missing attributes are matched by default which creates false positives when using lenient configurations for cross-project aggregations.
v
If you request an attribute and it is not provided, I'm relatively sure that resolution will fail. If you not request an attribute that is provided, that is deemed compatible with only very awkward ways to deal with it differently.
m
If you request an attribute and it is not provided, I’m relatively sure that resolution will fail.
It didn’t look like it but my example is quite involved so maybe there’s more. I’ll try to reproduce on a smaller one
parent module:
Copy code
plugins {
  id("org.jetbrains.kotlin.jvm")
}
child module
Copy code
val resolvable = configurations.create("resolvable") {
    isCanBeResolved = true
    isCanBeConsumed = false

    attributes {
        attribute(Attribute.of("foo", Named::class.java), objects.named("bar"))
    }
}

dependencies {
    add("resolvable", project(":root"))
}

resolvable.files.forEach {
    println(it.absolutePath)
}
output:
Copy code
> Configure project :child
/Users/mbonnin/git/test-publishing/parent/build/libs/parent.jar
/Users/mbonnin/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/2.0.0/b48df2c4aede9586cc931ead433bc02d6fd7879e/kotlin-stdlib-2.0.0.jar
/Users/mbonnin/.gradle/caches/modules-2/files-2.1/org.jetbrains/annotations/13.0/919f0dfe192fb4e063e7dacadee7f8bb9a2672a9/annotations-13.0.jar
Looks to me like my “foo” attribute is silently ignored?
v
Iirc, this might be because you did not add your attribute to the attribute scheme which should always be done.
Try adding it and see what happens then
m
Yea, was trying that at the moment but without luck so far.
Does it need to be in the consuming project or producing? Or both maybe?
v
Puh, don't hurt my poor brain at 1:30 am 😄
😄 1
Probably both
But I think at least on the consumer, if not
m
Same with
Copy code
dependencies {
    add("resolvable", project(":root"))
    attributesSchema {
        attribute(myAttribute) 
    }
}
(and same thing without the project line in the other module)
Trying to add a compatibility rule right norw
v
compatibility rule should be irrelevant, especially if one of the sides is
null
, for that the rule will never be asked iirc
Try adding it to the attributes schema before you use the attribute
👍 1
(I know, just try 🙂)
🙂 1
m
Nope, same thing
Copy code
val myAttribute = Attribute.of("foo", Named::class.java)
dependencies {
    attributesSchema {
        attribute(myAttribute) {
            compatibilityRules.add(MyCompatibilityRule::class.java)
        }
    }
}

val resolvable = configurations.create("resolvable") {
    isCanBeResolved = true
    isCanBeConsumed = false

    attributes {
        attribute(myAttribute, objects.named("bar"))
    }
}

dependencies {
    add("resolvable", project(":root"))
}
MyCompatibilityRule
is called if the producer has the attribute but if it’s missing it doesn’t get called so I don’t get a chance to say
incompatible()
v
What does
dependencyInsight
say for that dependency?
til 1
Iirc it shows requested and provided attributes
but if it’s missing it doesn’t get called
Yep, that's what I said, if either side is
null
it will not be called. Because requested but not provided should always fail, not requested but provided should always succeed.
m
Yea
Copy code
$ ./gradlew :child:dependencyInsight --configuration resolvable --dependency :root

> Configure project :child
/Users/mbonnin/git/test-publishing/root/build/libs/root.jar
/Users/mbonnin/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/2.0.0/b48df2c4aede9586cc931ead433bc02d6fd7879e/kotlin-stdlib-2.0.0.jar
/Users/mbonnin/.gradle/caches/modules-2/files-2.1/org.jetbrains/annotations/13.0/919f0dfe192fb4e063e7dacadee7f8bb9a2672a9/annotations-13.0.jar

> Task :child:dependencyInsight
project :root
  Variant runtimeElements:
    | Attribute Name                     | Provided     | Requested |
    |------------------------------------|--------------|-----------|
    | org.gradle.category                | library      |           |
    | org.gradle.dependency.bundling     | external     |           |
    | org.gradle.jvm.environment         | standard-jvm |           |
    | org.gradle.jvm.version             | 17           |           |
    | org.gradle.libraryelements         | jar          |           |
    | org.gradle.usage                   | java-runtime |           |
    | org.jetbrains.kotlin.platform.type | jvm          |           |
    | foo                                |              | bar       |

project :root
\--- resolvable

A web-based, searchable dependency report is available by adding the --scan option.
v
Hm, seems I still did not fully get that attributes-chaos into my thick skull 😕
m
And now if I add my own “consumable” configuration to root including the attribute, it’s working well
Copy code
> Task :child:dependencyInsight
project :root
  Variant consumable:
    | Attribute Name | Provided | Requested |
    |----------------|----------|-----------|
    | foo            | bar      | bar       |
Yea, that’s hairy
That “dependencyInsight” task is pretty cool though, thanks for sharing!
👌 1
v
Argh
image.png
😢 1
🎯 1
m
And there’s no API to change that?
It’s a pain for PI-compatible aggregations
Just when I thought I had things figured out 😅
Or I need to find an attribute name that is present in all outgoingVariants in all projects
It was working so far with
Usage
but I just bumped in a case where it’s not set (I think it’s a sources variant)
v
Use an attribute that already exists on all relevant projects? Just like the jacoco report aggregation sets category to verification and then verification type to jacoco results for example
m
I don’t own the projects though so it feels dangerous...
v
I think it’s a sources variant
Another task you might have missed,
outgoingVariants
shows you exactly what variants are there including their capabilities and attributes
m
Yep, that one I had 🙂
👌 1
Very useful!
1
I think I’ll just skip project isolation for now. Or if users want project isolation, they’ll have to list all their dependencies manually
v
And there’s no API to change that?
None I'm aware of right now, short of using a component metadata rule to set the attribute on all variants or something like that. But that might only work on external module dependencies, not project dependencies. Maybe require your plugin to be applied everywhere and set your attribute on all "other" variants 🤷‍♂️
🤯 1
👆 1
m
That might actually work! I’m using
beforeProject {}
already so might as well apply a plugin... And then cross fingers for no side effects 🤞
Not sure I want to go that deep though 😅
Well, time to sleep, tomorrow is another day, thanks again for the help!
👌 1
t
Component metadata rule to make sure every variant has the attribute is the way to go IMO (or of course declaring that attribute on the producer side)
👀 1
m
Modifying other people variants makes me a bit nervous though...
v
Don't you love this tickling feeling? 😄
m
Feeling alive!
👌 1