This message was deleted.
# dependency-management
s
This message was deleted.
e
Basically, I get the message:
Copy code
Could 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:
Copy code
> Task :outgoingVariants
--------------------------------------------------
Variant outConf
--------------------------------------------------

Capabilities
    - sample:emptygradle:1.0 (default capability)
Attributes
    - my-special-attribute = something
Artifacts
    - build/archive/output.zip (artifactType = zip)
And the registration is called with:
Copy code
val 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"))
    }
}
j
Yes this is always tricky. The API isn’t clean here imo. What you need to be aware of is that there are: 1. Attributes on Variants (Configurations) that are used to match the Variants (Configurations) in the 1st phase of dependency resolution 2. Attributes on Artifacts which are used to access artifacts in the 2nd phase of dependency resolution But both use the same
Attribute
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 resolvedTransform configuration. And then do this when accessing the artifacts:
Copy code
val resolveTransform by tasks.registering {
    inputs.files(resolvedSimple.incoming.artifactView {
       attributes.attribute(specialAttribute, objects.named("target"))
    }.files)
    doLast {
        inputs.files.forEach {
            println(it)
        }
    }
}
v
Ah, so the documented example works because with
artifactTypes.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?
e
Thanks. This helps a lot. I indeed can make it work by putting more on the outgoing configuration:
Copy code
val 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:
Copy code
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?
Copy code
"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"
        }
      ]
    }
  ]
And if I put "my-artifact-attribute" on the variant, I end up with the same problem as before: Gradle rejects the variant.
j
Cool. I forgot about the fact that you can put attributes on the artifacts directly on the producer side! Unfortunately, I think publishing them is a missing feature. 😕 I don’t know if there is an issue for that already. But it sounds like that should be added. As far as I recall, you can only set attributes that are published on the “outgoing variants” And then all the attributes from the variants are automatically put on all artifacts of the variant. The issue with that is, that the attribute that you set is now also used for matching variants. Although the use case is only about the artifacts. (And you might break the matching in a weird way although you don’t want to change it at all. Which is happening in your example.)
artifactTypes.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…
In your example, when do you get the matching error?
If you add the
attributes.attribute(artifactAttribute, "archive")
to the configuration instead of the artifact, it should be published.
And matching should still work, as you do not say anything about the
artifactAttribute
on the consumer side. It should be ignored then.
If you only use
artifactAttribute
in the artifact view (and not the configurations) on the consumer side.
e
Yes, but it would be really cool to be able to use directly the configuration. I think I see what is possible now. I also feel something is missing for this to be seamless, especially in the scenario where you want resolution to work both as a project dependency through a composite build or as an external dependency. Thank you again for the help and precisions.
So, in conclusion, for the artifact transform to work at all on an external dependency, you HAVE to rely on adding attributes on artifacts. And the different ways I see it can be done is: • artifactTypes • artifactView Component metadata rules seem to be out, because I don't see any API that can add attributes on artifacts. So it would be great if we could publish those artifacts when publishing a component with Gradle?
v
aritfactView
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.
e
Yes. My post wasn't very precise, indeed. For clarity, let's call the attribute that will be used to trigger the transformation
folder-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.
👍 1
v
• 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 dependencies
Are 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. 🙂
👍 1
j
especially because the artifactView no longer carries task dependencies
That’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”.
👍 1
@Sergey Igushkin Maybe you find this interesting. I remember that you were asking about feedback on issues with variant-aware dependency management some time ago. Do you happen to know if there is an issue already about “publishing artifacts attributes”? (cc @Louis Jacomet)
e
> 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.
👌 2