Thanks for great feedback! 2 sie 2014 02:05 "Adam Murdoch" <[email protected]> napisał(a):
> > On 1 Aug 2014, at 9:18 pm, Szczepan Faber <[email protected]> wrote: > > More thoughts on conflict resolution, Adam-style :) > > 1. The starting use case is avoiding conflicting guava and > google-collections on the same classpath. We'll enable that by > offering an API to declare that a module (group+name) > replaces/supersedes/deprecates a different module (group+name). This > use is a starting point to more interesting use cases which I will > describe in later. While designing the DSL and the behavior we need to > be cautious about those other use cases, so that we can develop this > feature incrementally. We will use guava an collections as an example. > > 2. In a standalone project, a guava-collections issue is not that > problematic, it can be sorted out by editing the build file (change > the dependencies, add an 'exclude', add force rule, etc.). The use > case is getting interesting in on an enterprise level, where the > organisation wants to solve the problem in the shared plugin suite, > instead of getting each team solve the problem on their own. > Well-known deprecations like collections-guava could be even encoded > in Gradle out of the box, but that's a different topic. > > > Absolutely we should. There are some backwards compatibility implications > if we were to add more meta-data about certain modules over time. > > > 3. Current APIs for influencing the dependency resolution (forcing > versions, dependency resolve rules) offer only "unconditional" > manipulation to the dependencies that are being resolved. Let's map > the collections-guava problem to the current API: we could force > collections to become guava via dependency resolve rule. The effect > is: > - only collections in graph - guava is used > a) only guava in graph - guava is used > b) both collections in graph - guava is used (collections are forced > into guava) > c) only collections in graph - guava is used > This is what I mean by 'unconditional' manipulation - if collections > appear in graph it is _always_ replaced with guava. And this is not > quite what we want... > > 4. We want the replacement rule to get activated only when both > collections and guava are in the graph. > > > I would think about it somewhat differently. We don’t want to > conditionally activate some rule here. Instead, we want to use some facts > about collections and guava to both detect conflicts between the two and > also to resolve such conflicts. > > So, collections was replaced by guava. This is a fact that is true > regardless of whether one or both or neither of them are in the graph. > > This fact implies a few further facts: > > - Collections and guava conflict with each other. They provide the same > API and shouldn’t both be present in a classpath. When resolving a > dependency graph, we have to select one of these to use. > - Guava is newer than collections, so if we happen to resolve conflicts by > selecting the newer of two conflicting modules, we should select the guava > module over the collections module. > > We’d use these facts in other places. For example, when reporting on > dependencies that are out-of-date, we should consider versions of guava as > candidate upgrades for any dependencies on collections. Same when we’re > offering suggestions in the IDE or when generating a dependency lock file. > > For this use case, what we’re after is a DSL to let you state these facts > about various modules. We don’t want an DSL where you provide imperative > code that implements the mechanics of detecting or resolving the conflicts. > This allows us to implement everything efficiently and also to reuse these > facts in other contexts beyond resolving a dependency graph. > > > If only collections appear in > the graph - there is no need to replace it with guava. Perhaps guava > and collection is not the best example because early guava is > completely backwards compatible with latest collections. So in theory, > we could unconditionally replace collections with an early version of > guava via existing dependency resolve rule API. However, let's imagine > that replacement source and target are not that quite compatible or > that someone is using an _early_ version of collections which is not > quite compatible with _early_ guava. > > > Compatibility is a different concern. > > You can think of the different versions of a given module in the same way > as replacement of one module by another. If I have A version 1.2 and A > version 1.3, then we can say 'A version 1.2 was replaced by A version 1.3’, > with the same implications. > > All we’re doing with replacement is saying something about the history of > some thing. This doesn’t say anything about its compatibility over time. > Or, more accurately, this doesn’t say anything more than what the version > of a thing says about compatibility. > > Similar to the history of a thing, there are also facts about the > compatibility of a thing over time that we want to use as input to > resolving a dependency graph, as part of resolving conflicts. If I have A > and B in the dependency graph and they conflict, and A is newer than B, > then I can select A only if A is compatible with B. Otherwise I have to > either fail the resolve or find some C that is newer than A and B and that > is compatible with both of them. > > We’d also use facts about compatibility when resolving a graph where > things are provided - eg the android SDK or the Windows API or whatever: If > I have A and B is in the dependency graph and they conflict, and A is > provided (and so can’t be substituted) and A is not compatible with B, then > I have to fail the resolve. > > Facts about compatibility are useful in other places beyond dependency > resolution. For example, when updating a dependency lock file, we might > skip candidates that are not compatible with what we’re currently using. Or > when offering suggestions in the IDE we might distinguish between > candidates that are compatible and not compatible. > > > > 5. The implementation is tricker than our current forcing/dependency > resolve rules. Consider dependency graph traversal scenarios: > - first we encounter collections, we happily use it and continue > traversing. > - then we encounter guava - oups - we need to evict already resolved > module collections and continue traversing. (It's more complicated > than that, as we resolve conflicts only when we cannot traverse any > further). > > In fact, it does not really differ from the standard, version-based > conflict resolution. I wanted to point out the difference between > existing dependency resolve rules implementation. The latter does not > need to consider already-resolved modules. > > > That’s right. The things we currently call ‘dependency resolve rules’ are > dependency (edge) replacement rules. They don’t have anything with conflict > handling. They’re very much not ‘everything that affects dependency > resolution’ rules. > > > 6. Other features to consider: > - module is replaced with a set of modules ('spring' -> > 'spring-core', 'spring-aop', etc.) > - set of modules is replaced with a single module (I don't know any > real example for that) > - api module and impl module use consistent version > > 7. Given that there are more ways to influence the dependency > resolution, and specifically, to replace/force certain dependencies > with others we should think about improving diagnostics on why certain > dependency ended up in the graph and why this particular version is > used. Our reports and API already provides 'selection reason' > information but I'm afraid it's not enough. A dependency can be > manipulated during resolution by different rules, applied by different > plugins and a flat information like 'dependency resolve rule was used' > is not enough IMHO. There can be a stream of actions that affects the > selected version of a dependency. > > > This is something the general-purpose model rules has to address, and so > the plan is that the ad hoc rule handling that we currently have in a few > places in dependency management will be replaced by the general-purpose > stuff, and will pick up the insight and reporting goodness that it provides. > > > 8. I made up my mind about the DSL. I'm easy and I can totally be > convinced to something else. Yet, here's my current preference: > > a) I would reuse some existing low-level, imperative API, like the > component rules or dependency resolve rules, for example: > > eachDependency { details -> > if (details.target.name == 'google-collections') { > details.replacedBy ‘com.google.guava:guava' > > > The eachDependency() method is used to replace one dependency edge with > another. It doesn’t make any sense to say something about the history of a > module here. > > If we were to add a low level mechanism, it should be given the meta-data > for a given module and allow the rules to say things like: > > - This module conflicts with modules with these ids. > - This module is newer than modules with these ids. > - This module is or is not compatible with modules with these ids. > > > } > } > > b) I would add some higher level api that would use a). For example: > > dependencies { > components { > > > modules("com.google.collections:google-collections").deprecatedBy("com.google.guava:guava") > //or replacedBy(), supersededBy() > } > } > > Thoughts? > > > We should only add the high level API. > > > On Fri, Aug 1, 2014 at 11:30 AM, Szczepan Faber <[email protected]> > wrote: > > This is a typo, the spec is work in progress. Initially we thought > about 'replaces' but we've changed that into 'replacedBy' but I forgot > to reverse the source and target :) > > So: > > google-collections is replaced by guava > guava replaces google-collections > > Thanks for pointing this out. I'll do more speccing and get back to > the mailing list. > > Cheers! > > On Fri, Aug 1, 2014 at 9:38 AM, Heinrich Filter > <[email protected]> wrote: > > Hi Szczepan, > > I'm new around the Gradle community but would love to start contributing > more. Please feel free to point out any "conventions" that I miss. > > For me the "replacedBy" keyword is a bit confusing. In your example: > 'guava' was replace by 'google-collections'. In my mind that implies that > 'google-collection' is the *newer* dependency and not, as you state in the > doc, the older version. > > I would think that 'guava' rather *replaces* or *supersedes* > 'google-collections'. > > Or am thinking about this the wrong way? > > Regards, > Heinrich > > On 31 Jul 2014, at 3:27 PM, Szczepan Faber <[email protected]> wrote: > > Hey, > > I've started speccing improvements to conflict resolution here: > > https://github.com/gradle/gradle/blob/master/design-docs/conflict-resolution-spec.md > (there's a big DSL mock-up down the bottom). > > It would be good to start discussing the DSL additions here on the > mailing list. Please give feedback about the DSL or the use cases, > etc. I haven't decided myself on the DSL :) I'll keep you in loop on > how the design goes. > > Cheers! > -- > Szczepan Faber > Core dev@gradle; Founder@mockito > > --------------------------------------------------------------------- > To unsubscribe from this list, please visit: > > http://xircles.codehaus.org/manage_email > > > > > ********************************************************************** > This email and all content are subject to the following disclaimer > > http://content.momentum.co.za/content/legal/disclaimer_email.htm > > ********************************************************************** > > --------------------------------------------------------------------- > To unsubscribe from this list, please visit: > > http://xircles.codehaus.org/manage_email > > > > > > -- > Szczepan Faber > Core dev@gradle; Founder@mockito > > > > > -- > Szczepan Faber > Core dev@gradle; Founder@mockito > > --------------------------------------------------------------------- > To unsubscribe from this list, please visit: > > http://xircles.codehaus.org/manage_email > > > > > -- > Adam Murdoch > Gradle Co-founder > http://www.gradle.org > CTO Gradleware Inc. - Gradle Training, Support, Consulting > http://www.gradleware.com > > > >
