Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically created task of type Copy is always UP-TO-DATE

I've prepared a very simple script, that illustrates the problem I see using Gradle 1.7 (need to stick with it because of some plugins not yet supporting newer versions).

I'm trying to dynamically create tasks each of which corresponds to a file in the project directory. This works fine, but the tasks I create never get executed as soon as I assign them type 'Copy'.

Here is my problem build.gradle:

file('templates').listFiles().each { File f ->

    // THIS LINE DOES NOT WORK
    task "myDist-${f.name}" (type: Copy) {

    // NEXT LINE WORKS
    //task "myDist-${f.name}" {
        doLast {
            println "MYDIST-" + f.name
        }
    }
}

task distAll(dependsOn: tasks.matching { Task task -> task.name.startsWith("myDist")}) {
    println "MYDISTALL"
}

defaultTasks 'distAll'

in this way my tasks do not get executed when I call default task calling simply gradle:

MYDISTALL
:myDist-template1 UP-TO-DATE
:myDist-template2 UP-TO-DATE
:distAll UP-TO-DATE

BUILD SUCCESSFUL

If I remove type Copy from my dynamic task (uncommenting the line above), my tasks get executed:

MYDISTALL
:myDist-template1
MYDIST-template1
:myDist-template2
MYDIST-template2
:distAll

BUILD SUCCESSFUL

(You'll need to create a folder name templates in the same directory where build.gradle is located and put couple of empty files into there in order to run the test)

According to the debug output:

Skipping task ':myDist-template1' as it has no source files.

Skipping task ':myDist-template2' as it has no source files.

So how can I specify source files and make my Copy tasks execute? I've tried adding

from( '/absolute/path/to/existing/file' ) {
    into 'myfolder'
}

to the task body, I've tried assigning task's inputs.source file('/my/existing/file') with no success. Could you please advise on how to modify my simple script leaving dynamic task creation and keeping my dynamic tasks of type Copy?

Thank you!

Edit: All right, this way the task gets called:

file('templates').listFiles().each { File f ->
    task "myDist-${f.name}" (type: Copy) {
        from f
        into 'dist'
        doLast {
            println "MYDIST-" + f.name
        }
    }
}

but it looks I must always specify from/into. It doesn't suffice to do that in the doLast{} body.

like image 964
Sergey Shcherbakov Avatar asked Nov 27 '13 17:11

Sergey Shcherbakov


2 Answers

A Copy task only gets executed if it has something to copy. Telling it what to copy is part of configuring the task, and therefore needs to be done in the configuration phase, rather than the execution phase. These are very important concepts to understand, and you can read up on them in the Gradle User Guide or on the Gradle Forums.

doFirst and doLast blocks get executed in the execution phase, as part of executing the task. Both are too late to tell the task what to copy: doFirst gets executed immediately before the main task action (which in this case is the copying), but (shortly) after the skipped and up-to-date checks (which are based on the task's configuration). doLast gets executed after the main task action, and is therefore clearly too late.

like image 109
Peter Niederwieser Avatar answered Sep 21 '22 03:09

Peter Niederwieser


I think the following Gradle User Guide quote answers my question the best:

Secondly, the copy() method can not honor task dependencies when a task is used as a copy source (i.e. as an argument to from()) because it's a method and not a task. As such, if you are using the copy() method as part of a task action, you must explicitly declare all inputs and outputs in order to get the correct behavior.

like image 38
Sergey Shcherbakov Avatar answered Sep 18 '22 03:09

Sergey Shcherbakov