Why would a task, with several output directories, be considered up-to-date, if the directories don't exist yet?

Given the following in a task:

def testList = 'a'..'z'
        componentList = testList + testList + testList + testList + testList + testList
        outputs.files files(componentList.collect { "../../src/${it}/${winPlatform}" })
        println componentList.collect { "../../src/${it}/${winPlatform}" }
        println componentList.size()

The task executes as expected, because the ‘a’…‘z’ directories do not exist:

...
../../src/x/win_x64, ../../src/y/win_x64, ../../src/z/win_x64]
156
:oldBuildscripts:makeTree
Executing on the following components - [a, b, c, ...

The reason why I tried so many of the above was due to the fact that my project has over a 130 real source component directories that are created by this task. However, when I specify the legitimate list of component directories, the task is always seen as up-to-date.

[../../src/component1/win_x64, ../../src/component2/win_x64, ..., ../../src/component135/win_x64]
135
:oldBuildscripts:makeTree UP-TO-DATE

Even more puzzling is using a shorter list of components, such as the first 5, will execute the task:

outputs.files files(componentList[1..5].collect { "../../src/${it}/${winPlatform}" })
        println componentList[1..5].collect { "../../src/${it}/${winPlatform}" }
        println componentList.size()

Output from the above:

[../../src/component1/win_x64, ../../src/component2/win_x64, ../../src/component3/win_x64, ../../src/component4/win_x64, ../../s
rc/component5/win_x64]
135
:oldBuildscripts:makeTree
Executing on the following components - [...

Any ideas why this might be happening? This is the gradle wrapper with 1.9, executing on Windows 7 x64.

Are you saying that the task’s job is to create an empty directory structure?

No. It’s a script that creates a sub-directory, for each component, which contain several files/symlinks. But, I can’t programmatically determine that entire list. So, my best approximation of a good task result is to verify that all of the output directories exist. Also, in order to clean the task, we just have to delete this same list.

I haven’t verified this, yet, but, I think that I’m specifying an invalid value. I can only guess that this will cause the outputs to default to up-to-date. One of my strings may have a backslash in it (read in from an output stream). I’m trying to verify.

You may be looking for ‘outputs.dir’, rather than ‘outputs.file(s)’.

I wasn’t sure if outputs.dir could take a FileCollection. But, I assumed that I had that wrong because of the behavior that I’m seeing. Perhaps, I should still be using outputs.dir. More importantly, I should be specifying the FileCollection, properly. :wink:

You’ll have to call ‘outputs.dir’ once per top-level directory produced by the task, passing a ‘File’ or a ‘String’.

I’m still at a loss. Thank you for the suggestion of using ‘outputs.dir’. That does seem to be what I want.

However, I can’t, for the life of me, figure out why this won’t work. I verified that I’m able to create valid ‘File’ objects, via the elements of my ‘componentList’. But, I can’t explain why the following doesn’t work, e.g. it shouldn’t consider the task up-to-date:

build.gradle:
...
        componentList.each { outputs.dir file("../../src/${it}/${winPlatform}") }
...
output:
  $ gradlew makeTree
:oldBuildscripts:makeTree UP-TO-DATE
  BUILD SUCCESSFUL

But, this executes, properly:

build.gradle:
...
        componentList[0..133].each { outputs.dir file("../../src/${it}/${winPlatform}") }
...
  output:
  $ gradlew makeTree
:oldBuildscripts:makeTree
Executing on the following components - [...]
...
build.gradle:
...
        componentList[1..134].each { outputs.dir file("../../src/${it}/${winPlatform}") }
...
  output:
  $ gradlew makeTree
:oldBuildscripts:makeTree
Executing on the following components - [...]
...

Is there something about groovy’s list iteration which I’m not getting here? I even tested specifying the full range, ‘componentList[0…134]’, but that considers the task up-to-date, too. This is bizarre.

So, to clarify, the task is not configured correctly when I pass the entire list range, but it is configured correctly when I pass a sub-list.

I’m afraid I can’t say from here what’s going on. Why do you think the task shouldn’t be up-to-date? Are you deleting or modifying some files contained in output directories between runs?

Because the task never completed. It fails on the first component every time that it is run, due to an environment issue on my machine, which I’m also working on. E.g. only the first output directory exists.

The only way I can think of to get up-to-date for a task that never completed is if its inputs are empty.

I still think that the behavior is incredibly bizarre. Upon changing the outputs, it somehow sees the task as completed. Is it possible that it cached some early test that I did, when creating the task? Anyways, changing the inputs did the trick. It is now properly recognizing the task as out-of-date, with the complete list of the components specified as the outputs.dir(s). Thanks, Peter. I appreciate you pointing me in the right direction.

The idea is that a task that has no or empty inputs has nothing to do, and is therefore up-to-date. Not sure how exactly you were affected by this, but I’m glad that you were able to solve your problem.