Why is the closure in from method of Copy task evaluated in config phase?


I was expecting that when I use a closure as argument of from() method of Copy task, the closure will be evaluated lazily => just before the task will be executed.

This is however not the case. Why? Is it because Copy task is automatically evaluating from() arguments to determine dependencies?

The following execution will fail: gradle testCopyTask --console=plain

task generateSources {
    doLast {
        mkdir 'build'
        file('build/source.txt') << "Generated source"

task testCopyTask(type: Copy, dependsOn: generateSources) {
    doFirst {
        println "execution phase"

    from {
        println "evaluating closure"

    destinationDir = file('build')

the closure syntax for configuring Copy tasks is more or less just syntactical sugar in contrast to the doLast / doFirst closure blocks. One reason that these closures need to be evaluated “early” is, the information in the closure is required during coniguration time.

here’s one example:

task jar(...){

task myCopy(type:Copy){
     into ("temp")

     into ("sub) {
          from jar

the copy task configuration influences the task graph of the build. by having from jar in your config, you add an implicit dependency to the jar task here. if that closure would be evaluated just before the myCopy task is executed, it would be to late in time as the task graph was already calculated.

hope that helps,

Although this isn’t the question that you asked, the task should fail regardless of when the closure is evaluated. The List of lines returned by readLines() doesn’t make sense as a from argument. Swinging the other direction, the task runs successfully with only file('build/source.txt') and not readLines(), which would be a valid from value.

Actually the from closure is evauluated when building the task graph, which is a phase right between configuration time and execution time. We generally don’t mention that since it rarely makes a difference.

It has to be evaluated at that point to make sure we’re getting all task dependencies right. It is however only evaluated if the task is part of those that need to be run for that build.

Your case stretches the basic copy task beyond its limit. I suggest you write a custom task which takes the sources.txt as an input file and then uses the project.copy() method inside the task action.

@Rene The closure you showed is a different one which will be evaluated at configuration time. It is meant to configure a subspec. The closure that @crazyjavahacking showed is a lazy file collection on the other hand which is evaluated when building the task graph.

Well it actually does make sense. The file contain a list of files that need to be packaged into zip file. This is specific to our business.

Waw, thanks for the investigation and deep explanation with a proposal!

Indeed. sorry, oversight on my side