Deterministic ordering of dependencies on the classpath

Hi,

coming from maven world you are actually able to control classpath order as of version 2.0.9 maven uses pom order for classpath so you can manipulate it freely and it has proven very useful in the past for us.

From what I read (and I may be wrong) Gradle doesn’t give any guarantees about the classpath ordering.

Can someone point me to the right direction how to implement similar behavior as Maven provides? Meaning preserves the ordering inside dependencies block. Even if it means writing a plugin or whatever.

Thanks

1 Like

This isn’t necessary. The order inside the dependencies { } block is preserved.

You are kind of right. Dependency ordering is preserved, but for example all compile dependencies will be before testCompile dependencies no matter of how you order them.

Depending on the nature of your conflict you could also exclude the dependency from ‘testCompile’ rather than force the correct one higher on the classpath.

Yes basically that is what we eventually… prepending specific dependencies as discussed in other topics on the forum.

Hi there,

I have noticed that if you add the dependency programmatically, i.e. project.dependencies.add(‘compile’, ‘my.group:my-artifact’) order is not preserved.

Not only that but although it evaluates to the same thing across invocations, it’s not the same on different machines. Do you have any ideas / recommendations how to deal with this apart from sorting the compileClasspath and setting it explicitly on each compile task?

The reason i have to do the programmatic dependency addition is that i am migrating a very old build that uses maven2 + ant still and in order to keep exactly the same resolution rules while migration is in progress i still read dependencies from pom (via ant-maven plugin) and set them to gradle not as files but gradle managed ones to be able to pull sources etc.

Thanks

1 Like

This doesn’t seem to be true? If there’s any relationship between their order in the build file and their order in the classpath, I can’t work out what it is.

Have you considered fixing your classpath so that there’s one of each resource/class?

You could identify the duplicates via:

task findDuplicates {
   doLast {
      Map<String, List<File>> pathMap = [:] 
      configurations.runtime.each { file ->
         FileTree tree = file.directory ? fileTree(file) : zipTree(file) 
         tree.visit { FileVisitDetails fvd ->
            if (!fvd.directory) {
               String path = fvd.path
               List<File> fileList = pathMap[path]?:[] 
               fileList << fvd.file
               pathMap[path] = fileList
            } 
         } 
      } 
      pathMap.each { path, fileList ->
         if (fileList.size() > 1) {
            println "Found duplicate $path in $fileList" 
         } 
      } 
   } 
} 

That is difficult if you are using third-party commercial libraries which contain older implementations of (say) commons-lang classes. It’s awkward, but it happens.

You could remove the classes from the offending jar as part of your build.

configurations {
   jarCleanup { transitive = false } 
}
dependencies {
   jarCleanup 'foo:dirty-dependency:1.0'
   compile files({ tasks['cleanupJar'] }) 
} 
task cleanupJar(type: Jar) {
   archiveBaseName = 'clean-dependency' 
   from zipTree(configurations.jarCleanup.singleFile)
   exclude 'offending/package/**' 
}

I wish to find some evidence of this in the API, but I can’t find anything. Traversing the dependency tree is done using sets, not lists. For example, we start with:

task printDeps {
    doLast {
        for (def rd in configurations.toArray()) {    // toArray() looks promising
            println(rd)
            if (!rd.isCanBeResolved()) continue
            def rc = rd.getResolvedConfiguration()    // This returns a ResolvedConfiguration
            printResolvedConfiguration(rc)
        }
    }
}

From ResolvedConfiguration, it’s only sets, not lists. The rest of the code for the example is in this gist. It’s sets all the way down. I understand that order should not matter, but I am working in an environment where controlling the order is a requirement.

It’s probably an ordered set then.
But there is no interface for ordered set.
I’m also pretty sure order within the dependencies block is preserved.
And while in an optimal world order shouldn’t be important, it in reality is.
And I think Gradle also is aware of that and thus does not use lib/* for example for the classpath in the generated start scripts, but instead lists all the dependencies in order.