Why up-to-date skipping works bad?

This is my build.gradle file

ext.testFileName = 'lol.txt'
ext.testFile = file(ext.testFileName)
  task writeA {
    inputs.file testFile
    outputs.file testFile
    doLast {
        def cout = new OutputStreamWriter(new FileOutputStream(testFile))
        cout.println('A')
        cout.flush()
    }
}
  task writeB {
    inputs.file testFile
    outputs.file testFile
    doLast {
        def cout = new OutputStreamWriter(new FileOutputStream(testFile))
        cout.println('B')
        cout.flush()
    }
}

When I run

gradle writeA writeB

for the first time it works as expected:

:writeA
:writeB
  BUILD SUCCESSFUL

For the second time its output is:

:writeA
:writeB UP-TO-DATE
  BUILD SUCCESSFUL

And for the others:

:writeA UP-TO-DATE
:writeB UP-TO-DATE
  BUILD SUCCESSFUL

Now file ‘lol.txt’ contains ‘A’. But I expected ‘B’. And no UP-TO-DATE, of course. What am I doing wrong?

Someone else might be able to give you technical reasons why this is behaving the way it is. However, I can’t imagine that Gradle can make any intelligent decision about “up to date” for a task that both reads and writes the same file. In addition, you not only have one task doing this, you have TWO tasks, both reading and writing the same file.

I don’t believe that tasks should read and write the same file, and I don’t think that multiple tasks should write to the same file.

Actually, behavior, described in User Guide, must handle this situation graceful. Of course, it’s a strange example, but Gradle also fails in much more simple situations. I used this variant because I see no way to implement this up-to-date check to get such results. This means, I can’t imagine why ‘writeB’ task is executed only once, for example. No bugs such as ‘Okay, let’s check all files before any task execution’ or ‘Omg, I’ve checked this file already, I think it’s up-to-date for all tasks’ can lead to this surprising actions.