Combining eachFile & filter results in failure

It seems that when doing something like the following in a Copy task is fine

filter PrefixLines, prefix : '// '
filter ConcatFilter, prepend : file('src/templates/foo.txt')

but when adding eachFile i.e.

filter PrefixLines, prefix : '// '
filter ConcatFilter, prepend : file('src/templates/foo.txt')

eachFile { fcd ->
}

it results in an UnsupportedOperationException

with a stack trace similar to

Caused by: java.lang.UnsupportedOperationException
    at org.gradle.api.internal.file.copy.DefaultFileCopyDetails.getFile(DefaultFileCopyDetails.java:60)
    at org.gradle.api.internal.BeanDynamicObject$MetaClassAdapter.getProperty(BeanDynamicObject.java:166)
    at org.gradle.api.internal.BeanDynamicObject.getProperty(BeanDynamicObject.java:109)
    at org.gradle.api.internal.file.copy.DefaultFileCopyDetails_Decorated.getProperty(Unknown Source)
    at build_3vv0w84xogl5x3kepn02o7b6z$_run_closure2$_closure4.doCall(/Users/schalkc/Projects/Genius/jde-rules/build.gradle:68)
    at org.gradle.api.internal.ClosureBackedAction.execute(ClosureBackedAction.java:67)
    at org.gradle.api.internal.file.copy.CopyFileVisitorImpl.processFile(CopyFileVisitorImpl.java:55)
    at org.gradle.api.internal.file.copy.CopyFileVisitorImpl.visitFile(CopyFileVisitorImpl.java:44)
    at org.gradle.api.internal.file.collections.jdk7.Jdk7DirectoryWalker$1.visitFile(Jdk7DirectoryWalker.java:78)
    at org.gradle.api.internal.file.collections.jdk7.Jdk7DirectoryWalker$1.visitFile(Jdk7DirectoryWalker.java:51)
    at org.gradle.api.internal.file.collections.jdk7.Jdk7DirectoryWalker.walkDir(Jdk7DirectoryWalker.java:51)
    at org.gradle.api.internal.file.collections.DirectoryFileTree.walkDir(DirectoryFileTree.java:143)
    at org.gradle.api.internal.file.collections.DirectoryFileTree.visitFrom(DirectoryFileTree.java:127)
    at org.gradle.api.internal.file.collections.DirectoryFileTree.visit(DirectoryFileTree.java:117)
    at org.gradle.api.internal.file.collections.FileTreeAdapter.visit(FileTreeAdapter.java:109)
    at org.gradle.api.internal.file.CompositeFileTree.visit(CompositeFileTree.java:58)
    at org.gradle.api.internal.file.copy.CopySpecActionImpl.execute(CopySpecActionImpl.java:37)
    at org.gradle.api.internal.file.copy.CopySpecActionImpl.execute(CopySpecActionImpl.java:24)
    at org.gradle.api.internal.file.copy.DefaultCopySpec$DefaultCopySpecResolver.walk(DefaultCopySpec.java:498)
    at org.gradle.api.internal.file.copy.DefaultCopySpec$DefaultCopySpecResolver.walk(DefaultCopySpec.java:500)
    at org.gradle.api.internal.file.copy.DefaultCopySpec$DefaultCopySpecResolver.walk(DefaultCopySpec.java:500)
    at org.gradle.api.internal.file.copy.DefaultCopySpec.walk(DefaultCopySpec.java:322)
    at org.gradle.api.internal.file.copy.DelegatingCopySpecInternal.walk(DelegatingCopySpecInternal.java:206)
    at org.gradle.api.internal.file.copy.CopySpecBackedCopyActionProcessingStream.process(CopySpecBackedCopyActionProcessingStream.java:36)
    at org.gradle.api.internal.file.copy.DuplicateHandlingCopyActionDecorator$1.process(DuplicateHandlingCopyActionDecorator.java:44)
    at org.gradle.api.internal.file.copy.NormalizingCopyActionDecorator$1.process(NormalizingCopyActionDecorator.java:56)
    at org.gradle.api.internal.file.copy.FileCopyAction.execute(FileCopyAction.java:35)
    at org.gradle.api.internal.file.copy.NormalizingCopyActionDecorator.execute(NormalizingCopyActionDecorator.java:52)
    at org.gradle.api.internal.file.copy.DuplicateHandlingCopyActionDecorator.execute(DuplicateHandlingCopyActionDecorator.java:42)
    at org.gradle.api.internal.file.copy.CopyActionExecuter.execute(CopyActionExecuter.java:38)
    at org.gradle.api.tasks.AbstractCopyTask.copy(AbstractCopyTask.java:83)
    at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:75)
    at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.doExecute(AnnotationProcessingTaskFactory.java:227)
    at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.execute(AnnotationProcessingTaskFactory.java:220)
    at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.execute(AnnotationProcessingTaskFactory.java:209)
    at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:585)
    at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:568)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:80)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:61)

Placing the filters inside the eachFile closure results in the same issue.

Tested with 2.5 - 2.9

Tracked this down with the help or @mrhaki. DefaulFileCopyDetails.java contains the following code

public File getFile() {
    if (filterChain.hasFilters()) {
        throw new UnsupportedOperationException();
    } else {
        return fileDetails.getFile();
    }
}

This explains the exception seen. I am goign to assume that this is intended behaviour and not a bug. However, this is not stated in the documentation of the Copy or AbstractCopyTask or FileCopyDetails.

I’m sorry I don’t have an answer to your questions, but it looks like this behaviour was introduced by https://github.com/gradle/gradle/commit/988e40aa in related to fixing https://issues.gradle.org/browse/GRADLE-786 .

That’s 6 years ago and if no one complained until now, then it is probably not wortwhile fixing. I’ll leave it to your Gradle Inc. boys and girls to decide. In mean-time I have created a PR where the docs for FileCopyDetails are updated.