Multiple from statements in Copy not evaluated in order

Hello, I am trying to merge multiple files from different directories into one target location (excluding duplicates… the first one copied should win). I’ve noticed that if you include a closure within your from method, the order of from statements may execute randomly.

The following is a code example. When I run the ‘debug’ task, I would expect all the outputs to have the file from folder a. Curiously though… out2 has the file from folder b. So it appears within out2, folder b is copied before folder a (even though a is specified first).

FYI… I am using gradle version 2.10

Output:
out1: I am file a!
out2: I am file b!
out3: I am file a!
out4: I am file a!

task init << {
        mkdir('a')
        def file1 = file('a/test.txt')
        file1.text = "I am file a!"

        mkdir('b')
        def file2 = file('b/test.txt')
        file2.text = "I am file b!"
}

task run(dependsOn: 'init') << {

        copy {
                duplicatesStrategy = DuplicatesStrategy.EXCLUDE
                from('a')
                from('b')
                into 'out1'
        }

        copy {
                duplicatesStrategy = DuplicatesStrategy.EXCLUDE
                from('a') {
                        include "test.txt"
                }
                from('b')
                into 'out2'
        }

        copy {
                duplicatesStrategy = DuplicatesStrategy.EXCLUDE
                from('a')
                from('b') {
                        include "test.txt"
                }
                into 'out3'
        }

        copy {
                duplicatesStrategy = DuplicatesStrategy.EXCLUDE
                from('a') {
                        include "test.txt"
                }
                from('b') {
                        include "test.txt"
                }
                into 'out4'
        }
 }

task debug(dependsOn: 'run') << {
        println "out1: " + file('out1/test.txt').text
        println "out2: " + file('out2/test.txt').text
        println "out3: " + file('out3/test.txt').text
        println "out4: " + file('out4/test.txt').text
}

I can see from the source code that closures are added to childSpecs whereas others are added directly to sourcePaths. From what I can see the sourcePaths are seen before the childPaths when walk()ing the CopySpec which explains the behaviour you are seeing

So, whilst unexpected, at least the behaviour is deterministic. As a workaround you could use closures for all from declarations until the bug is fixed. You could use empty closures {}

The workaround mentioned by @Lance_Java should work. Essentially, ensure all specs are either root or children but not both. I’ve raised GRADLE-3397 in the mean time to track this.