A copy task to regenerate a file: dependsOn taskX but skipped despite taskX.didWork

Hello,

My use case:

  • a copy task generates a file from a template file using the ‘expand’ filter.

  • another task defines the values of the properties which are expanded.

  • the copy task dependsOn this task

Essentially I want the result file to be re-generated each time the property values change.

My problem: the copy task runs the first time, but subsequently doesn’t, claiming to be “up to date”. The file never gets updated from then on.

A cut down example. Assume a file called ./dir/a.template containing:

$date

And a ./build.gradle like so:

project.ext.defs = [date: ‘unset’]

task setDate << {

project.ext.defs.date = new Date();

println “-- set date to $project.ext.defs.date”;

}

task expandTemplate(type: Copy, dependsOn: setDate) {

from ‘dir’

include ‘a.template’

into ‘dir’

rename ‘a.template’, ‘a.date’;

expand(project.ext.defs)

doLast { println “-- expandTemplate running, date is $project.ext.defs.date”; }

}

task checkSetDate(dependsOn: setDate) << {

println “-- setDate.didWork = $setDate.didWork”

}

task all(dependsOn: [checkSetDate, expandTemplate]) << {

println “-- a.date contains ${file(‘dir/a.date’).text}”

}

The first run’s output looks like this:

% gradle -i all

Starting Build

Settings evaluated using empty settings script.

Projects loaded. Root project using build file ‘/home/nick/tmp/build.gradle’.

Included projects: [root project ‘tmp’]

Evaluating root project ‘tmp’ using build file ‘/home/nick/tmp/build.gradle’.

All projects evaluated.

Selected primary task ‘all’

Tasks to be executed: [task ‘:setDate’, task ‘:checkSetDate’, task ‘:expandTemplate’, task ‘:all’]

:setDate

Task ‘:setDate’ has not declared any outputs, assuming that it is out-of-date.

– set date to Fri Dec 13 14:37:36 GMT 2013

:checkSetDate

Task ‘:checkSetDate’ has not declared any outputs, assuming that it is out-of-date.

– setDate.didWork = true

:expandTemplate

Executing task ‘:expandTemplate’ due to:

Output file /home/nick/tmp/dir/a.date has been removed.

– expandTemplate running, date is Fri Dec 13 14:37:36 GMT 2013

:all

Task ‘:all’ has not declared any outputs, assuming that it is out-of-date.

– a.date contains Fri Dec 13 14:37:36 GMT 2013

BUILD SUCCESSFUL

The second run’s output shows gradle skipping the expandTemplate task:

gradle -i all

Starting Build

Settings evaluated using empty settings script.

Projects loaded. Root project using build file ‘/home/nick/tmp/build.gradle’.

Included projects: [root project ‘tmp’]

Evaluating root project ‘tmp’ using build file ‘/home/nick/tmp/build.gradle’.

All projects evaluated.

Selected primary task ‘all’

Tasks to be executed: [task ‘:setDate’, task ‘:checkSetDate’, task ‘:expandTemplate’, task ‘:all’]

:setDate

Task ‘:setDate’ has not declared any outputs, assuming that it is out-of-date.

– set date to Fri Dec 13 14:38:05 GMT 2013

:checkSetDate

Task ‘:checkSetDate’ has not declared any outputs, assuming that it is out-of-date.

– setDate.didWork = true

:expandTemplate

Skipping task ‘:expandTemplate’ as it is up-to-date.

Skipping task ‘:expandTemplate’ as it is up-to-date

:expandTemplate UP-TO-DATE

:all

Task ‘:all’ has not declared any outputs, assuming that it is out-of-date.

– a.date contains Fri Dec 13 14:37:36 GMT 2013

BUILD SUCCESSFUL

I had thought that because the expandTemplate depends on setDate, it would check setDate.didWork and run if it was true, but apparently not.

How would I make sure a file generated using expand() gets regenerated each time the expanded properties change?

Thanks.

The up-to-date check is solely based on a task’s declared inputs and outputs. The fact that ‘Copy’ filter arguments aren’t automatically declared as inputs is a known limitation. You should be able to declare them yourself though - try something like ‘myCopyTask.inputs.property “templateProperties”, myTemplateProperties’.

Thanks for the quick reply.

Your suggestion does indeed solve my real case, I believe, because unlike the cut-down example I gave, my property is set by the time the configuration phase runs. (It generates an .NSI config file from filenames and versions in order to build an NSI installer.)

However, this workaround doesn’t work with the test example I gave because the setDate task changes the date property after the config phase. I don’t see any documented means to pass a closure or something similar to Task.inputs.property to allow it to check at build time. How would one handle this kind of case?

Also, this implies that it isn’t always true that tasks will run if tasks they depend others which on .didWork. Is that by design or accident?

Trying now, passing a closure to Tasks.inputs.property does seem to work as expected. Sorry, I should have checked before I sent my earlier message.

It might help others if the documentation for the Copy task mentioned this, and possibly that it doesn’t observe Task.didWork on its dependencies (if that is by design).

Incidentally, I’m using gradle v1.9.

How would one handle this kind of case?

Many Gradle APIs accept closures, and this is also the case for ‘task.inputs.property’ (see Javadoc). Apart from this, there are several other callback techniques (e.g. ‘project.afterEvaluate’) that allow plugins to defer configuration of tasks.

Also, this implies that it isn’t always true that tasks will run if tasks they depend others which on .didWork. Is that by design or accident?

It’s by design. As I explained earlier, whether a task is up-to-date is solely determined based on the task’s declared inputs and outputs. The fact that a depended-on task had some work to do doesn’t mean that the current task has some work to do.