Slackbot
10/06/2022, 7:19 PMEmmanuel Guerin
10/07/2022, 6:04 AMCould not determine the dependencies of task ':sub:resolveTransform'.
> Could not resolve all task dependencies for configuration ':sub:resolvedTransform'.
> Could not resolve project :.
Required by:
project :sub
> No matching variant of project : was found. The consumer was configured to find attribute 'my-special-attribute' with value 'target' but:
- Variant 'outConf' capability sample:emptygradle:1.0:
- Incompatible because this component declares attribute 'my-special-attribute' with value 'something' and the consumer needed attribute 'my-special-attribute' with value 'target'
The result of calling outgoingVariants on the dependent project is:
> Task :outgoingVariants
--------------------------------------------------
Variant outConf
--------------------------------------------------
Capabilities
- sample:emptygradle:1.0 (default capability)
Attributes
- my-special-attribute = something
Artifacts
- build/archive/output.zip (artifactType = zip)
Emmanuel Guerin
10/07/2022, 6:06 AMval specialAttribute = Attribute.of("my-special-attribute", Named::class.java)
dependencies {
attributesSchema {
attribute(specialAttribute)
}
registerTransform(ArchiveUnzipTransform::class.java) {
from.attribute(specialAttribute, objects.named("something"))
to.attribute(specialAttribute, objects.named("target"))
}
}
Jendrik Johannes
10/07/2022, 6:36 AMAttribute
interface. The same attribute can be used in both places, but it is really hard to get it to a certain behaviour. What you are seeing now is the first phase of resolution failing, because the variants cannot be matched. The two attribute values are not compatible. At this point, transforms are not even considered yet (because they only work on the artifacts later).
This behavior is not intuitive and I stumble over this every time I attempt to use transforms.
The best setup is (if it is sufficient for your use case) to not use a second resolvable configuration. Instead, use an ArtifactView, when accessing the artifacts (2nd phase of resolution). Then the different attribute values only play a role for the artifact retrieval (after the variant selection has already been performed).
In your sample, you can remove the val resolveTransform by tasks.registering {
inputs.files(resolvedSimple.incoming.artifactView {
attributes.attribute(specialAttribute, objects.named("target"))
}.files)
doLast {
inputs.files.forEach {
println(it)
}
}
}
Vampire
10/07/2022, 7:00 AMartifactTypes.getByName
you set the attribute on the artifacts, not on the configuration and when resolving the configuration the attribute is ignored because the producer configuration does not have the attribute at all, right?
Is there also a way to "fix" the producer side of OP's example, so that the attribute is set on the artifact, not on the configuration?Emmanuel Guerin
10/07/2022, 7:47 AMval artifactAttribute = Attribute.of("my-artifact-attribute", String::class.java)
val variantAttribute = Attribute.of("my-variant-attribute", String::class.java)
val outConf by configurations.creating {
isCanBeResolved = false
isCanBeConsumed = true
attributes {
attribute(variantAttribute, "my-variant")
}
outgoing {
attributes.attribute(artifactAttribute, "archive")
artifact(packing.flatMap { it.archiveFile })
}
}
Now I can consume it with:
val resolvedTransform by configurations.creating {
isCanBeConsumed = false
isCanBeResolved = true
attributes {
attribute(variantAttribute, "my-variant")
attribute(artifactAttribute, "folder")
}
}
dependencies {
registerTransform(ArchiveUnzipTransform::class.java) {
from.attribute(artifactAttribute, "archive")
to.attribute(artifactAttribute, "folder")
}
}
Now for the extra bonus question: I'm trying now to publish the original project using maven-publish and an adhoc component associated with the outgoing configuration. But when I do so, the module.json only contains the attributes declared on the configuration, not on the artifacts. Is there a way to have artifact attributes in Gradle metadata?
"variants": [
{
"name": "outConf",
"attributes": {
"my-variant-attribute": "my-variant"
},
"files": [
{
"name": "emptygradle-1.0.zip",
"url": "emptygradle-1.0.zip",
"size": 55189,
"sha512": "5c9cc018ea1bb3120fccb580c8cd6e6c10e01fc09290bff4aafc176f460dda166ec6e9cbfd1dbf945fc62c021547cfdcc2a6b40efd9005c0aef671317d2af1ff",
"sha256": "319fc9ea8b5286008767cbfea6768d6f3f1b9662a673035b58752e8f55ba6116",
"sha1": "38df937c82a209b8890c55c76f986c13a6ca8be0",
"md5": "12429093d4a5d57405d1c9ae2c62b258"
}
]
}
]
Emmanuel Guerin
10/07/2022, 7:52 AMJendrik Johannes
10/07/2022, 7:56 AMartifactTypes.getByName..
thingy is a way to “patch” this on the consumer side. You can give certain attributes to certain artifacts. But the API is very limited (I think it’s incomplete, it feels like something that was done to get it working somehow but then it was never revisited). Some time ago I was trying to improve that, but never got to a point to really specify what it should be (https://github.com/gradle/gradle/pull/12089). But maybe instead of improving that, there should be the option to publish the attributes on artifacts (and fix them with component metadata rules if needed). Then the artifactTypes
thing could probably be removed…Jendrik Johannes
10/07/2022, 7:57 AMJendrik Johannes
10/07/2022, 7:57 AMattributes.attribute(artifactAttribute, "archive")
to the configuration instead of the artifact, it should be published.Jendrik Johannes
10/07/2022, 7:58 AMartifactAttribute
on the consumer side. It should be ignored then.Jendrik Johannes
10/07/2022, 7:59 AMartifactAttribute
in the artifact view (and not the configurations) on the consumer side.Emmanuel Guerin
10/07/2022, 8:21 AMEmmanuel Guerin
10/07/2022, 10:28 AMVampire
10/07/2022, 11:37 AMaritfactView
is not adding an attribute to an artifact, it requests an attribute for an artifact.
artifactTypes
can add attributes to a certain artifact type that you can then request later on.
The other way to add attributes to artifacts is to have them inherit the attributes from the configuration.
In your example both, the configuration and the artifact have something
.
But if you request target
from the configuration it cannot match the configuration as transform is only for artifact.
If you request target
through the artifact view, the configuration is matched as the attribute is not requested and then the artifact transform can make the artifacts have the wanted attribute.Emmanuel Guerin
10/07/2022, 12:01 PMfolder-bundling
. It can have 2 values: folder
and ziptree
.
I want to register a transformation that can transform an artifact from a ziptree
to a folder
.
At this time, it seems to me:
• an external component on a Maven repository can not have the attribute "ziptree" set on one of its variants (even if it actually is a ziptree) in its Gradle metadata. If it is set, it prevents Gradle from selecting the variant if the requested attribute value is "folder".
• there is no way for an external component to declare attributes on its artifacts in its Gradle metadata.
• to levarage a transformation one has to:
◦ use a resolvable configuration with the proper attribute set, but combine it with an artifactTypes declaration to add the necessary artifact attribute.
◦ use a suitably configured artifactView of a configuration without the attribute set.
The first one links the transformation to a particular artifactType (extension), which I think shouldn't be necessary.
The second one makes usage complex, especially because the artifactView no longer carries task dependencies, if the external dependency was substituted with a project dependency by a composite build for example.
My feeling is that what is missing to be able to perfectly use the artifact transform is to be able to publish what is necessary on the external component (the fact that the artifact is a tartree), so that the client project running the transform doesn't have to repeat it but can discover it from the metadata themselves.Vampire
10/07/2022, 12:12 PM• an external component on a Maven repository can not have the attribute "ziptree" set on one of its variants (even if it actually is a ziptree) in its Gradle metadata. If it is set, it prevents Gradle from selecting the variant if the requested attribute value is "folder".That's halfway right, halfway wrong. You can have the attribute on the variant, but if you want
folder
, then you must not request it on the configuration but use an artifact view to trigger the artifact transformation after the configuration was successfully selected.
• there is no way for an external component to declare attributes on its artifacts in its Gradle metadata.That seems to be correct according to Jendrik
• to levarage a transformation one has to:
◦ use a resolvable configuration with the proper attribute set, but combine it with an artifactTypes declaration to add the necessary artifact attribute.
◦ use a suitably configured artifactView of a configuration without the attribute set.I don't think this is fully correct, I'm not sure whether you mean the correct thing though. If you have the attribute on the resolvable configuration, the variant will not selected, no matter if you use
artifactTypes
or not, because the attributes of the configuration do not match and no transformation can change that.
If the consumable configuration and artifact do not have any attributes set, you can on the consumer side use artifactTypes...
to add attributes to the artifacts like minified = false
or exploed = false
to then use an artifact transform to get them to the value you want to have.
In that case the attribute is only set on the artifact, so you can specify the attribute in the resolvable configuration, as the consumable configuration does not have the attribute and thus it is ignored.
But if the consumable configuration has a value for the attribute, it has to match or be compatible by compatibility rule.
especially because the artifactView no longer carries task dependenciesAre you sure? I'd really wonder if that is the case.
to be able to publish what is necessary on the external component (the fact that the artifact is a tartree),Exact, as Jendrik said. If there is no feature request for that, you should probably open one. 🙂
Jendrik Johannes
10/07/2022, 1:00 PMespecially because the artifactView no longer carries task dependenciesThat’s not correct. An artifactView is an additional lazy layer on top of the configuration. All dependencies are preserved. I think this is a good concept in general. I agree on the part that it should be possible to publish attributes for (and only for) artifacts. And then also have an API in component metadata rules to adjust these if necessary. After this (good and interesting) discussion my feeling is that this is the missing piece to make things clearer and avoid “hacks”.
Jendrik Johannes
10/07/2022, 1:04 PMEmmanuel Guerin
10/10/2022, 6:24 AM> especially because the artifactView no longer carries task dependencies
Are you sure? I'd really wonder if that is the case.Sorry, you're right, it seems to work. And all your remarks are in synch with my current understanding, even if my posts may suggest otherwise. I'll try to open a feature request for artifact attribute publication and modification, if it is indeed the direction that Gradle needs to go.