Thanks Adam, that's the input I was looking for. Some more questions / feedback below.
On Mon, Apr 21, 2014 at 9:39 AM, Adam Murdoch <adam.murd...@gradleware.com>wrote: > > On 20 Apr 2014, at 7:38 am, Szczepan Faber <szczepan.fa...@gradleware.com> > wrote: > > I was asked by the client to perform a quick spike on conflict resolution > rules. I decided spike it first before kicking off a discussion. Here're my > findings. > > Intro. > > Currently, a version conflict means that there are modules with the same > group+name but different version. However, there is also a different class > of conflicts - different modules (different group or name) are conflicting > and only one of them should be chosen. In the spike, the ResolutionStrategy > a) allows declaring that certain modules are conflicting and b) allows to > choose preferred module version in case of such conflict. > > Some specific use cases I found so far: > > 1. 'com.google.collections:google-collections' and > 'com.google.guava:guava' are conflicting, prefer guava. > 2. 'org.jboss.netty:netty' and 'io.netty:netty*' are conflicting, prefer > io.netty. > 3. 'org.springframework:spring' and 'org.springframework:spring-*' are > conflicting, prefer 'org.springframework:spring-*' (for example, > spring-core). > 4. 'kafka:kafka_2.8.2' VS 'kafka:kafka_2.10' VS 'kafka:kafka' are > conflicting, prefer _2.10 (say) > 5. A team in an organization decided to extract a standalone project out > of a bigger project. The group id of the modules moved to a new project > needed to change. Now there's a risk that consumers will have problems with > conflict resolution. > > > You can boil this down to 3 use cases: > > 1. A given component moves to some new coordinates. This would be #1 and > #2 above. > 2. A given component is repackaged, by splitting it into a set of smaller > components. This would be #3 and #5 above. Groovy 1.x -> 2.x is another > example of this. > 3. A given component is build for multiple versions of some runtime. This > would be #4 above, where Scala is the runtime. > > You can generalise #1 and #2 into a 'replaces' abstraction: > > - com.google.guava:guava replaces com.google.collections.google-collections > - io.netty:netty-all replaces org.jboss.netty:netty > - io.netty:(everything except -all and -parent) replace > org.jboss.netty:netty > - my.org:(a, b) replace my.old.org:ab > > This can also be used to model Maven relocations too. > > Given we know that a replaces b, we can do two things: > > 1. Detect conflicts. Only one of a or b can be present in the result. > 2. Resolve the conflict. The result should include a and not b. > I like where this model is going. Not sure if 'replaces' is the right word. At first sight I associate it with 'forcing', e.g. an unconditional replacement. However, I cannot think about anything that is better. > We should model #3 using a 'runtime' abstraction. > > Given that we know that 'a_2.10' is 'a' with runtime Scala 2.10 and > 'a_2.8' is 'a' with runtime Scala 2.8, we can: > > 1. Detect conflicts. Only one of 'a', 'a_2.10' and 'a_2.8' can be present > in the result. > 2. Resolve the conflict by selecting the components with the "best" Scala > runtime. > 3. Substitue 'a_2.8' with 'a_2.10' or 'a' when the "best" Scala runtime is > not Scala 2.8. > > By "best" Scala runtime this would be either the Scala version we're > building for, which we can infer from our direct dependencies, or the > latest Scala runtime that appears in the graph if we're not building for > Scala. > I like it. > For all these use cases, these facts are something about the component > that is independent about which graphs the component happens to appear in. > So, they should be declared for the component meta-data, using, say, > component meta-data rules. I wouldn't use resolution strategy for this. > I think it is a good idea to model this at the component level. The current limited solutions to the use cases I've seen, implemented via dependency resolve rules, were always applied to all configurations. This indicates the use cases are independent of the graphs. I'll probably spike 'replaces' abstraction at some point. Cheers! -- Szczepan Faber Principal engineer@gradle; Founder@mockito Join us for Gradle Summit 2014, June 12th and 13th in Santa Clara, CA: http://www.gradlesummit.com