Support depending on multiple variants of the same component This commit changes the resolution engine so that it is possible to resolve multiple variants of the same component when using variant-aware dependency management.
Before, in order to have 2 dependencies on the same component but using different variants, one had to use explicit configuration dependencies. Now, with this change, it is possible to have two dependencies on the same component, but with different attributes. Those components would resolve to different variants.
Special treatment is applied when attributes are declared on constraints: in this case, we _merge_ the constraint attributes, and make sure that the edge is computed using the merged attributes. Should they be incompatible, the build would fail as before.
Consistently use component level attributes Component-level attributes were only used if the component metadata was using Gradle metadata. This was particularly confusing, as it was possible to define a component- level attribute in a component-metadata rule, but it would be ignored during matching. It was possible, however, to add attributes on variants of Ivy or Maven metadata, but then the error messages in case there wasn't any match was even more confusing:
"Because there are no configurations with attributes"
This commit modifies the resolution engine so that it's possible to use component level attributes independently of whether the underlying component metadata was constructed from Ivy, Maven or Gradle metadata. It also changes the error messages to mention either "configuration" or "variant" depending on whether the matching strategy was using variant-aware dependency management *or* legacy configurations.
It was confusing because we have the `IMPROVED_POM_SUPPORT` flag which activates variant construction from Maven metadata. This meant that in practice, if the flag was active, we were using variants, but still the component level attributes were not taken into account. If the flag wasn't active, then we would fail with the error above, despite the fact we had rules on "configuration backed variants".
The separation of configuration/variant in error messages makes it easier for us to understand in which case we are, since this wasn't always obvious. If we see "variant", then now we know that the selection failed using the variant-aware matching. If we see "configuration", then we know it's using the legacy mode.
This commit also needed to make the difference between "this component has no variant" and "this component doesn't provide any mapping to variants", therefore the introduction of the `Optional` on `getVariantsForTraversal`. This gives us the opportunity to give the correct error message in case of failure using the legacy mode.
Last but not least, this introduces a change in the attributes visible on variants **and** artifacts. Before this commit, dependending on whether metadata was Gradle metadata, the "status" attribute would be found or not. Now, the component level attributes are _always_ merged to variant and artifact attributes, which means that it's consistent independently of the format. This can be a breaking change, but a low risk one.