This message was deleted.
# dependency-management
s
This message was deleted.
s
I do realize that the point of making dependencies variant aware is to avoid doing the selection by hand 🙂 . But there could be cases where I’d want to explicitly declare the variant to be consumed.
v
You cannot select a configuration for published dependencies. If you have a multi-project build where one project depends on another, you can use the configuration shortcut instead of going the full attributes way. Unless consumers outside the build should be able to retrieve that variant, then you have to use the full way. You often select the variant by hand, for example by requesting a specific capability. Only in some cases like when building Gradle plugin variants for different Gradle versions, or building different variants for different Java versions or similar, where the according attributes are already set by Gradle or some plugin, the selection can happen automatically.
s
Consider the following published depdency
Copy code
A
\____B
\____C

D
\___A
I have
A
which depends on
B
and
C
. I also publish all variants of the 3 libraries. So the
A(debug)
depends on
B(debug)
and
C(debug)
and similarly for the
release
variant. Now the debug variant of
D
depends on the debug variant of
A
and transitively
B
and `C. And similarly for release. Can I request for the
release
of
A
in the debug variant of
D
? Can you also elaborate a bit on what do you mean by configuration shortcut? Would that be using
configurationName "group:name:version:classifier@extension"
?
v
That sounds like Android, I have no idea about Android. But I guess you can choose a different variant, yes. No, again, you can only depend on a configuration within a multi-project build like
implementation(project(path = ":B", configuration = "foo"))
. And this form is a simplified shortcut for a proper published variant that is also only available within the multi-project build and not published in GMM or other metadata
s
Yes this is an android related question 🙂. However is it possible for me to specify/modify a single attribute which is being asked for? For example, the android gradle plugin adds an attribute of type
"com.android.build.api.attributes.BuildTypeAttr": "debug",
for each variant. Can I not explicitly specify an attribute type while requesting a dependency as a part of the dependencyNotation
v
Yes you can
s
Would you have some documentation showcasing this handy? I’m not clear on how to create an attribute value for using as a part of the dependencyNotation
v
Something like
Copy code
implementation("a:b:1") {
    attributes {
        attribute(...)
    }
}
I don't have the docs at hand, I'm on mobile
s
Ah no problem .That helps. Thank you! The signature for the attribute creation is
attribute(Attribute<T> key, T value)
. And I’m unable to figure out what I can use for the value here. nvm . Let me do some more digging. Thank you very much for help 🙂
v
Either the Android plugin exposes its somewhere, or you can alternatively do
Attribute.of(...)
iirc
x
Our attribute interfaces are public. You can see it here: https://developer.android.com/reference/tools/gradle-api/7.2/com/android/build/api/attributes/BuildTypeAttr Use the method on the
Companion
class to create the right instance of
Atttribute<>
(especially important for the flavor attribute)
👌 2
s
Thank you for that!. I’ve been playing around with this for a while, but with no luck. Continuing the same example as above ; I can declare the dep as
Copy code
debugImplementation("com.foo.libs:A:1.0.2") {
        attributes {
            attribute(BuildTypeAttr.ATTRIBUTE, objects.named(BuildTypeAttr::class.java, "release"))
        }
    }
However running dependencyInsight for
C
in the project still shows the debug variant being selected.
I’ve had some progress here though . I now see the release variant of
A
in my dependency graph, however that seems to bring in the
debug
variant of
C
which IMO should not be the case? Since
A-release
depends on
C-release
.
x
You need to put the attributes on the
Configuration
that is resolved. These (
debugImplementation
) are just where you set your dependencies. The ones that are resolved are going to be
<variant>CompileClasspath
and
<variant>RuntimeClasspath
v
That's not correct, you can set attributes for specific dependencies like I showed him. This overrides the attributes set on the configuration. The remaining problem is, that not A-release depends on C-release, but A depends on C, and the configuration
debugImplementation
says it needs
debug
so for A it is manually forced to use
release
while C still uses what comes from the configuration which is
debug
.
s
So updating this for all configurations, does resolve my issue
Copy code
configurations.all {
    resolutionStrategy.dependencySubstitution {
        substitute(module("com.foo.libs:A:1.0.2")).using(variant(module("com.foo.libs:A:1.0.2")) {
            attributes {
                attribute(
                    BuildTypeAttr.ATTRIBUTE,
                    objects.named(BuildTypeAttr::class.java, "release")
                )
            }
        })
}
And repeating this for each of the transitive dependencies does bring in all release variants, however this would require me to know all the transitive dependencies that
A
exposes and set them to the same variant. Is this expected though? I had assumed that choosing the
release
variant of
A
would ensure that compatible
release
variants of both
B
and
C
would also be selected
v
Again, A-release does not depend on C-release. A depends on C (or maybe A-release depends on C, but not A-release on C-release, look at the Gradle Module Metadata to see it) It would be different when A depended on a specific different capability of C. But in this case this is not the case, the capabilities are the same, just the attributes are different. The configuration says that for all dependencies the variant with attribute
debug
should be chosen and you only override it for A.
s
Also running a dependency insight, gives me this line
Copy code
> Task :D:dependencyInsight
com.foo.libs:A:1.0.2
   variant "releaseVariantDefaultApiPublication" [
      com.android.build.api.attributes.BuildTypeAttr  = release (compatible with: debug)
v
So?
s
I think I understand this now, The GMM would need to explicitly declare different capabilities such that A-release depends on B-release. Since this only specifies different attributes, which does not mean they are incompatible. Hence gradle still chooses the debug variants for other dependencies .
v
It chooses the debug variant because the debug variant is what is requested by the configuration
s
would the variant selected for transitive dependencies depend on what was requested by the configuration or the variant compatible with the variant of the specified dependency?
To get this working the way I expected it to. i.e. have release variants depend explicitly on other release variants and similarly for debug, I had to update the dependencies in
A
to segregate the release and debug dependencies explicitly by specifying the buildType attributes. This added an additional filter of the buildTypeAttr in the dependencies block of GMM for that variant. This then leads to my expected behaviour of only changing the variant for
A
to also change those for the transitive dependencies. Pretty much what you’ve been saying all along that the GMM defined that
A
depends on
C
and not that A-release depends on C-release.
👌 1
x
That's not correct, you can set attributes for specific dependencies like I showed him. This overrides the attributes set on the configuration.
Ah yes I misread. I thought it was on
debugImpl
rather than on the dependency. I'm really curious how that behaves for the transitive dependencies? It seems like it'd still have to use the attributes defined on the configuration because these transitive dependencies can be present in other part of the graph, so that could lead to weird results
doing
configurations.all
works but it will apply on many, many more
Configuration
objects than needed and use extra memory. If you can do it only on the compile/runtime classpath configurations of the variant that interest you, that would be better.
would the variant selected for transitive dependencies depend on what was requested by the configuration or the variant compatible with the variant of the specified dependency? (edited)
When using attributes on
Configuration
Gradle will resolve each nodes with the attributes of the consuming configuration. What the parent nodes do in their own resolution has no impact (so if a > b > c and somehow b select a different variant for c, it won't matter when a makes its selection of c)
Again not sure what happens if we add attributes on a specific dependency.
s
So if I were to add attributes to the specific dependencies too, then dependency resolution fails if there are multiple variants requested in a configuration. Yes I’d used
configurations.all
temporarily. will change this to only apply to the specific configurations
This makes me think that I should specify an attribute selector for deps that I’d want to be accessible transitively? If I do not explicitly specify specify variants , then it is possible that the variant of my direct dependency would be incompatible with the the transitive deps whose variant is defined by the configuration and not what I specified in my dependencies.
v
The typical case is, that the configuration defines the attributes, because you typically want all code in debug variant or all code in release variant. And (I don't know about Android but in other projects) you typically do not resolve the configurations you declare the dependencies on. For example you have a non-resolvable configuration
deps
where you declare the dependency on A, and then two resolvable configurations
releaseClasspath
and
debugClasspath
which do not get dependencies declared directly but extend from
deps
. Those configurations then define that they want the
debug
or the
release
variant of all the libs.
👍 1