Cédric Champeau

Add validation step for multiple variant selection

This commit adds a graph validation step, which checks that if

multiple variants of a single component are selected, they have

compatible attributes.

Currently, compatible means _identical_. If we want to improve

this further, next steps would be to do this during selection

instead of validation, and potentially substitute one selected

variant with the other when one is fully compatible with the other

(in other words, all its common attributes are compatible with

the other variant). This, however, would likely have a performance

impact in selection.

Add validation step for multiple variant selection

This commit adds a graph validation step, which checks that if

multiple variants of a single component are selected, they have

compatible attributes.

Currently, compatible means _identical_. If we want to improve

this further, next steps would be to do this during selection

instead of validation, and potentially substitute one selected

variant with the other when one is fully compatible with the other

(in other words, all its common attributes are compatible with

the other variant). This, however, would likely have a performance

impact in selection.

Add test case for composite builds

Add test case for composite builds

Add test case for composite builds

Fix expectations of integ tests

Fix expectations of integ tests

Fix expectations of integ tests

Support requested capabilities on external dependencies

This commit adds support for having requested capabilities

part of the module component selector, for external dependencies.

This means that if a component is using Gradle metadata, we can

read requested capabilities and honor them during selection.

This reworks where requested capabilities are stored, and in

particular moves them to the `ComponentSelector`, making them

properly part of the identity of a dependency. As such, two

dependencies requiring two different variants by using distinct

capabilities will now properly appear as two different dependencies

in the dependency graph, instead of two variants of the same

dependency.

  1. … 49 more files in changeset.
Support requested capabilities on external dependencies

This commit adds support for having requested capabilities

part of the module component selector, for external dependencies.

This means that if a component is using Gradle metadata, we can

read requested capabilities and honor them during selection.

This reworks where requested capabilities are stored, and in

particular moves them to the `ComponentSelector`, making them

properly part of the identity of a dependency. As such, two

dependencies requiring two different variants by using distinct

capabilities will now properly appear as two different dependencies

in the dependency graph, instead of two variants of the same

dependency.

  1. … 49 more files in changeset.
Support requested capabilities on external dependencies

This commit adds support for having requested capabilities

part of the module component selector, for external dependencies.

This means that if a component is using Gradle metadata, we can

read requested capabilities and honor them during selection.

This reworks where requested capabilities are stored, and in

particular moves them to the `ComponentSelector`, making them

properly part of the identity of a dependency. As such, two

dependencies requiring two different variants by using distinct

capabilities will now properly appear as two different dependencies

in the dependency graph, instead of two variants of the same

dependency.

  1. … 49 more files in changeset.
Initial support for optional features

This commit introduces initial support for optional features, by

implementing a way for a dependency declaration (currently *only* in

the DSL) to request variants of the target component that provide one

or more capabilities.

Previously to this change, selection was (simplified) done like this:

1. find the target component

2. select the variant of the target component which matches the requested

attributes

Now, selection introduces another step:

1. find the target component

2. filter variants by eliminating those which do not provide the requested

capabilities

3. select the variant in this list which matches the requested attributes

Several changes had to be implemented:

First, component metadata rules calling `addCapability` will now return

a component which capabilities _include_ the default capability.

Second, attribute filtering is done in a secondary step, which means that

if there are no variant matching the requested capabilities, we will immediately

fail.

  1. … 44 more files in changeset.
Initial support for optional features

This commit introduces initial support for optional features, by

implementing a way for a dependency declaration (currently *only* in

the DSL) to request variants of the target component that provide one

or more capabilities.

Previously to this change, selection was (simplified) done like this:

1. find the target component

2. select the variant of the target component which matches the requested

attributes

Now, selection introduces another step:

1. find the target component

2. filter variants by eliminating those which do not provide the requested

capabilities

3. select the variant in this list which matches the requested attributes

Several changes had to be implemented:

First, component metadata rules calling `addCapability` will now return

a component which capabilities _include_ the default capability.

Second, attribute filtering is done in a secondary step, which means that

if there are no variant matching the requested capabilities, we will immediately

fail.

  1. … 44 more files in changeset.
Initial support for optional features

This commit introduces initial support for optional features, by

implementing a way for a dependency declaration (currently *only* in

the DSL) to request variants of the target component that provide one

or more capabilities.

Previously to this change, selection was (simplified) done like this:

1. find the target component

2. select the variant of the target component which matches the requested

attributes

Now, selection introduces another step:

1. find the target component

2. filter variants by eliminating those which do not provide the requested

capabilities

3. select the variant in this list which matches the requested attributes

Several changes had to be implemented:

First, component metadata rules calling `addCapability` will now return

a component which capabilities _include_ the default capability.

Second, attribute filtering is done in a secondary step, which means that

if there are no variant matching the requested capabilities, we will immediately

fail.

  1. … 44 more files in changeset.
Restore test, but with different expectation

Restore test, but with different expectation

Add test coverage for variant capability conflict resolution

Add test coverage for variant capability conflict resolution

Rename method for clarity

Rename method for clarity

Only 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.

Only 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.

Only 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.

Only 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.

Fail resolution if 2 variants provide the same capability

This commit makes sure that we fail resolution whenever two variants

of a component provide the same capability. However, it doesn't do so

for the implicit variant yet.

  1. … 5 more files in changeset.
Fail resolution if 2 variants provide the same capability

This commit makes sure that we fail resolution whenever two variants

of a component provide the same capability. However, it doesn't do so

for the implicit variant yet.

  1. … 5 more files in changeset.
Fail resolution if 2 variants provide the same capability

This commit makes sure that we fail resolution whenever two variants

of a component provide the same capability. However, it doesn't do so

for the implicit variant yet.

  1. … 5 more files in changeset.
Remove unnecessary `VariantNameBuilder`

Remove unnecessary `VariantNameBuilder`

Remove unnecessary `VariantNameBuilder`