fileTree as Incremental Task Input includes added directories

Hi,
I found what seems like a bug, or at least missing documentation.

I have an Incremental Task that uses a FileCollection (created with fileTree()) as input (@InputFiles).
fileTree is configured to scan a specific diretory only for files with specific extensions. Querying it with getFiles(), only lists files that match the include pattern.

But when used as input for the incremental task, any added directories get passed into IncrementalTaskInputs.outOfDate(). This caused a lot of hard to find errors, as my task was not capable of handling directories, since its input FileCollection was meant to only containe files.

Further testing also showed, that on first execution of a new task, only mathing files are delivered through outOfDate. But when a new, empty directory is added following that, it will also be indicated as a changed input.

Is this behaviour actually intended, am I using something wrong, or is this in fact a bug?

Sample code that allows reproducing this:

class TestTask extends DefaultTask {

	@InputFiles
	def getFiles() {
		return project.fileTree(dir: 'any/folder', include: '**/*any.type.of.file')
	}

	@TaskAction
	def exec(IncrementalTaskInputs inputs) {
		inputs.outOfDate {
			println it.file
		}
	}
}

task demo(type: TestTask)

Gradle Version: tested on 2.13, 2.14.1, 3.0-rc2, 3.1-nightly
Operating System: Windows 10/1607, Ubuntu 14.04
Does not seem like a regression, as it covers a wide range of versions. I only noticed it recently and do not recall what code I changed and did previously not test for it.

Of course, a workaround is rather easy with File.isDirectory(), but more consistent behavior would make thins a lot easier.

Regards,

Ramon

I feel this is expected behaviour. A FileTree includes the files matching the pattern and the directories too, including empty ones. So a change in an empty directory is a change to the FileCollection.

To exclude directories from the up to date check you could try

fileTree(dir: 'any/folder', include: '**/*.foo').files.findAll { it.isFile() }

Or to exclude empty directories you could try

fileTree('any/folder').matching {
   include '**/*.foo'
   exclude { FileTreeElement element ->
      element.isDirectory() && !element.file.listFiles
   } 
} 

I’m guessing the copy tasks do something similar with setIncludeEmptyDirs