How to override the version comparison mechanism?

I have three repositories. The first repository contains version 0.2.0-RELEASE. The second contains 0.2.0-NIGHTLY. And the third contains 0.2.0-LOCAL.

I found in the source code that gradle uses a lexicographical comparison. Gradle downloads version 0.2.0-RELEASE because “RELEASE”.compareTo(“NIGHTLY”) > 0 and “RELEASE”.compareTo(“LOCAL”) > 0

Does anyone know how to override this behavior?

You could define a custom ResolutionStrategy

configurations.all {
   resolutionStrategy {
      eachDependency { DependencyResolveDetails details ->
         def req = details.requested
         if (req.group == 'foo' && req.version.endsWith('-NIGHTLY') {
            details.useVersion doSomeTransformation(req.version)
         }
      }
   }
}

Unfortunately it does not work in my case. I don’t request the version that ends with “-NIGHTLY” or “-RELEASE”. I request the version 0.2.+

Ah, I thought you had a dependency conflict but you are referring to dynamic dependencies. I think this can be done via a ComponentSelectionRule

resolutionStrategy {
   componentSelection {
      Comparator<ComponentSelection> comparator = new MyFantasticVersionComparator()
      Map<String, Set<ComponentSelection>> grouped = [:]
      all { ComponentSelection selection ->
         String key = "${selection.candidate.group}:${selection.candidate.name}"
         def set = grouped[key]
         if (!set) {
            set = new TreeSet(comparator)
            grouped[key] = set
         }
         set<< selection
      }
      for (Set set : grouped.values()) {
         Iterator<ComponentSelection> iterator = set.iterator()
         for (int i = 0; i < set.size() - 1) {
            iterator.next().reject("Better version available")
         }
      }
   }
}
2 Likes

The code above no longer seems to work at least in Gradle 5.0. It seems as though it creates the Map and then goes directly to the for loop, skipping over the section adding to the items to the map. After that it finally processes the all closure statement. No items are ever added to the map.

Is there a better way to do this now? Is there away to override the SPECIAL_MEANINGS map in StaticVersionComparator or a way to provide a different comparator?

Here is what I have in my build.gradle:

configurations.all {
resolutionStrategy {
componentSelection {
Comparator comparator = new MyFantasticVersionComparator()
Map<String, Set> grouped = [:]
all { ComponentSelection selection ->
String key = “{selection.candidate.group}:{selection.candidate.module}”
def set = grouped[key]
if (!set) {
set = new TreeSet(comparator)
grouped[key] = set
}
set<< selection
}
for (Set set : grouped.values()) {
Iterator iterator = set.iterator()
for (int i = 0; i < set.size() - 1; i++) {
iterator.next().reject(“Better version available”)
}
}
}
}
}

I’m guessing that the timing has changed slightly meaning the execution of the “all” closure is delayed. I’m guessing that you can tweak it slightly to

resolutionStrategy {
   componentSelection {
      Comparator<ComponentSelection> comparator = new MyFantasticVersionComparator()
      Map<String, Set<ComponentSelection>> grouped = [:]
      all { ComponentSelection selection ->
         String key = "${selection.candidate.group}:${selection.candidate.name}"
         def set = grouped[key]
         if (!set) {
            set = new TreeSet(comparator)
            grouped[key] = set
         }
         set<< selection
      }
      boolean isFirst = true 
      all { ComponentSelection selection ->
         if (isFirst) {
            for (Set set : grouped.values()) {
               Iterator<ComponentSelection> iterator = set.iterator()
               for (int i = 0; i < set.size() - 1) {
                  iterator.next().reject("Better version available")
               }
            }
            isFirst = false    
         } 
      } 
   }
}

Looking at the source code in “DefaultVersionedComponentChooser.java” I don’t think this would ever work as it doesn’t loop through every version of the dependency unless you call reject on each one, at which point you would have nothing left.

What I am really looking for is somehow to override the versionComparator in that class.

I don’t think your solution works since execution of the all {} block is deferred. This seems to work though:

def allThings = [:]

configurations.foolib.resolutionStrategy {
  componentSelection {
    all { 
      def moduleId = candidate.moduleIdentifier
      def others = allThings[moduleId]

      if (!others) {
        others = []
        allThings[moduleId] = others
      }

      others.add(it)

        for (def thing : others) {
          if (COMPARE ALL VERSIONS HERE) {
            println "rejecting version ${thing.candidate.version}"
            thing.reject()
          }
      }
    }
  }
}

Thanks for new updates. I’m still naive gradle user. May I know where to add this block of solution? I have maven-to-gradle generated build.gradle file:

allprojects {
}

subprojects {
apply plugin: ‘java’
repositories {
}

sourceCompatibility = '1.8'

publishing {
    publications {
        maven(MavenPublication) {
            from(components.java)
        }
    }
}

tasks.withType(JavaCompile) {
    options.encoding = 'UTF-8'
}

}