Gradle / subprojects / dependency-management / src / ... / gradle / integtests / resolve / attributes
MultipleVariantSelectionIntegrationTest.groovy
a14cce0Add test coverage for variant capability conflict resolution
cc1f550Only allow more than one variant of a component if it has different capabilities
This commit significantly reworks how capabilities are handled, in particular with regards to accepting 2 different variants of the same component to appear in a graph. It is now allowed to have 2 variants of the same component if they have distinct capabilities. It shall be reminded that if a variant doesn't explicitly declare any capability, it is assumed to carry the _implicit capability_, which corresponds to the GAV of the component. Said differently, by default, all variants of a component are supposed to be incompatible with each other (they cannot appear in the same dependency graph). If 2 variants are indeed compatible, then they _must_ have different capabilities. This will be used to support optional dependencies, by making it possible to express that one has to choose a specific set of compatible variants to enable some feature. There are however a few issues that needed to be addressed in this commit. First of all, what we've just described is only true if we use variant-aware selection, and more specifically attribute-based variant selection. If not, then a legacy mode is used, and it's possible to have 2 "variants" of a component on the same graph. This is typically the case when there are dependencies on 2 different _configurations_ of a project: dependencies { api project(path: ':foo', configuration: 'conf1') api project(path: ':foo', configuration: 'conf2') } For backwards compatibility reason, even if the 2 configurations actually provide the same capability, this is allowed. Another case is when a cycle is found between a root component and a transitive dependency corresponding to the same component in a lower version. Last but not least, in the engine the implicit capability is treated specially for performance reasons: capability conflict resolution is very expensive, and adding an explicit capability to all components would trigger conflict resolution everywhere (to find out if other components provide the same capability). This leads to some optimizations in the code which are not necessarily easy to follow, but properly documented. |
![]() |