When are input/output snapshots taken?

I always thought that the input snapshot (the one that’s persisted between gradle invocations) is taken before task execution and the output snapshot is taken after task execution. However, I have an example that seems to contradict this assumption.

class FooTask extends DefaultTask {
    @Input
    def props = [:]
      @TaskAction
    def run() {
        println "Executing FooTask"
    }
}
  task foo(type: FooTask) {
    outputs.upToDateWhen { true }
      doFirst {
        props['fruit'] = 'cherry'
    }
}
  $ ./gradlew :it:foo  :it:foo  Executing FooTask

 BUILD SUCCESSFUL

 Total time: 3.613 secs  
"foo" will never be considered UP-TO-DATE. However, if I get rid of doFirst or clear the property in doLast, "foo" can be UP-TO-DATE.

If input snapshots are persisted BEFORE task execution, I don’t understand why that’s happening. However, if input snapshots are persisted AFTER, then it makes sense:

  1. First run. There’s no history. Input and output snapshots are taken and persisted after task execution. Input snapshot contains the “fruit” property. 2. Second run. Before task execution, a snapshot of the inputs is taken. It does not contain the “fruit” property, and as a result, gives a different hash than the snapshot taken at the end of the last run. So, task is not UP-TO-DATE. 3. Repeat step 2 ad infinitum. Task is never considered UP-TO-DATE.

Is my reasoning correct? If not, what is causing this behavior?

It is as you presume. Up-to-date checking is done (obviously) immediately before execution. Task execution state however, is persisted after execution, which in this case means when I go to run ‘foo’ I have no input properties, but when I compare that against the last time it ran, I see that there was a property called “fruit”, therefore the task cannot be considered up-to-date.

Okay, it’s great to have official confirmation. I don’t think this behavior is documented anywhere, certainly not the relevant section of the user guide [1]. Might I suggest it be added?

[1] https://gradle.org/docs/current/userguide/more_about_tasks.html#sec:up_to_date_checks

The text in the user guide is actually correct. Input files are snapshotted before task execution. Property values however are not persisted to history until after execution.

The property value behavior is simply the result of an implementation detail. We generally discourage configuration to be done during the execution phase for exactly this reason (it makes incremental builds unreliable).