Task triggered on input property change even though the property has identical value?


(Dawid Weiss) #1

I have a task class which declares (trimming some boilerplate):

class ClosureCompiler extends DefaultTask {
  @Input
  @Optional
  def args = []
    @TaskAction
  def compile() {
    println "BEF: " + args
    ...
    project.javaexec {
      ...
      args = localArgs
    }
    println "AFT: " + args
  }

When I run the following sequence:

mk clean && mk -i carrot2:dist > out1 && mk -i carrot2:dist > out2

This task is executed in the second run even though the arguments have not

changed at all. The output log says that:

“Executing task ‘:carrot2:minify’ due to: Value of input property ‘args’ has changed for task ‘:carrot2:minify’”

but when I println this parameter is it identical in both builds. Since it’s a list of values the ordering of values doesn’t change either (and I see it when I diff those output dumps that they’re identical).

Any clue how I can diagnose this or what I’m doing wrong?


(Dawid Weiss) #2

A logdiff of the two runs. Note the ‘BEF’ and ‘AFT’ blocks; the arg’s value seems to be identical?


(Dawid Weiss) #3

Oh, I’m now thinking that maybe these arguments are not strings and this may be confusing the hashing system? Is there a way to dump how the hashes are constructed from Input parameters?


(Dawid Weiss) #4

I’ve tracked this based on a custom injected snippet of code into gradle’s InputPropertiesChangedUpToDateRule. The problem is that arguments is empty when comparing the old value to the new one. I think it may have something to do with the fact that in the task that uses it I populate it in the doFirst call. It makes sense - it’s in the execution phase after all.

I would appreciate an explanation (or a pointer to the documentation) explaining how configuration/execution phases and each task’s doFirst/doLast/configuration closure block relate to each other, in particular with respect to evaluation of inputs/outputs hashes. I couldn’t find this information anywhere and it seems crucial for correct implementation of up-to-date aware tasks.


(Peter Niederwieser) #5

The up-to-date check is made immediately before the task is executed (or not). Hence a change to inputs in ‘doFirst’ isn’t visible to the check and should, if possible, be avoided.


(Dawid Weiss) #6

Thanks Peter, I see where I made a mistake. I still don’t know how to solve the problem I’m facing. If you take a look at this build it shows the issue:

configure(subprojects) { project ->
  task run(type: MyTask) {
    outputFile = file(buildDir.absolutePath + "/output.txt")
    // We want per-project definition of 'defines' property and we copy its value here.
    defines.each { args << it }
  }
}
  project("suba") {
  ext.defines = ["foo"]
}
  project("subb") {
  ext.defines = ["bar"]
}
  class MyTask extends DefaultTask {
  @OutputFile
  def outputFile
    @Input @Optional
  def args = []
    MyTask() {
    outputs.upToDateWhen { false }
  }
    @TaskAction
  def doRun() {
    println project.name + " => " + args
  }
}

During the configuration phase the property “defines” is not yet assigned when the subprojects closure is executed and it fails with an error (sensibly). I see a workaround in moving per-project configuration closures before subprojects configuration (doable). Things work then. But what if I want to keep the order presented here? Is there a way to express a configuration logic of a task that happens after all the configuration has been parsed but before the tasks are executed and inputs/outputs are hashed/compared with the previous state? This kind of works but it feels clumsy:

configure(subprojects) { project ->
  task run(type: MyTask) {
    outputFile = file(buildDir.absolutePath + "/output.txt")
    gradle.taskGraph.whenReady {taskGraph ->
      defines.each { args << it }
    }
  }
}

Just trying to wrap my head around the possible solutions, I appreciate your time.


(Dawid Weiss) #7

Just to complete – this also doesn’t work if I move ext.defines definition to a subproject’s build.gradle. Subproject files seem to be evaluated after the parent so the parent doesn’t see subproject’s properties.


(Dawid Weiss) #8

Read through the manual again and found this – evaluationDependsOnChildren. I missed it previously, sorry. It still doesn’t solve the all-in-one-file example but I think it’s a minor nuisance and it’s probably unsolvable since it’s a groovy script.


(Peter Niederwieser) #9

‘evaluationDependsOnChildren’ is a last resort. In the present case, I might do:

subprojects {
    task run(type: MyTask) { ... }
}
  project("suba") {
    run {
         args = ["foo"]
     }
}
  project("suba") {
    run {
         args = ["bar"]
     }
}

Some methods in the Gradle API accept a closure to allow you to defer evaluation. Another option (to be used sparingly) is to use ‘project.afterEvaluate { … }’ or ‘gradle.projectsEvaluated { … }’.


(Dawid Weiss) #10

Damn the most obvious answer eluded me… Brilliant, this is it – exactly what I was looking for. Don’t know why I wanted to push it via project properties. Thanks!