What's wrong with my inputs.* and outputs.* settings?

task jflex {
    inputs.source fileTree(
        dir:'src', exclude:'**/.*/**', include:'com/admc/jcreole/*.grammar')
    outputs.file new File('tmp/derived-src/com/admc/jcreole/CreoleScanner.java')
    doLast {
        ...
        javaexec {
            ...
        }
    }
}

I’ve been doing this for decades with Ant, but it’s not working with Gradle. The indicated source does exist and the indicated output does not exist-- how is Gradle concluding that this is up-to-date?

13:20:09.587 [DEBUG] [org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter] Determining if task ':jflex' is up-to-date
13:20:09.602 [DEBUG] [org.gradle.cache.internal.DefaultFileLockManager] Waiting to acquire Exclusive lock on cache directory D:\Data\jcreole\.gradle.0-milestone-5\taskArtifacts.
13:20:09.602 [DEBUG] [org.gradle.cache.internal.DefaultFileLockManager] Lock acquired.
13:20:09.634 [DEBUG] [org.gradle.api.internal.changedetection.DefaultFileCacheListener] Can cache files for directory 'D:\Data\jcreole\src' include 'com/admc/jcreole/*.grammar' exclude '**/.*/**'
13:20:09.649 [DEBUG] [org.gradle.api.internal.changedetection.DefaultFileCacheListener] Can cache files for file 'D:\Data\jcreole\tmp\derived-src\com\admc\jcreole\CreoleScanner.java'
...
[omitting detailed debug prints about cache locking]
13:20:09.727 [INFO] [org.gradle.api.internal.changedetection.DefaultTaskArtifactStateRepository] Skipping task ':jflex' as it is up-to-date.
13:20:09.743 [INFO] [org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter] Skipping task ':jflex' as it is up-to-date

(Removing the exclusion about hidden files, “/.*/”, has no effect).

I’ve simplfied the test case to this. Gradle still thinks it’s up-to-date even though the indicated outputs file does not exist:

inputs.file new File('src/com/admc/jcreole/CreoleParser.grammar')
    outputs.file new File('tmp/derived-src/com/admc/jcreole/CreoleScanner.java')

Following work-around works, but I sure wish I could use the documented Gradle features that were designed for just what I want to do.

jflex.onlyIf {
    File grammarFile = file('src/com/admc/jcreole/CreoleParser.grammar')
    File scannerFile =
            file('tmp/derived-src/com/admc/jcreole/CreoleScanner.java')
    (!grammarFile.exists()
            || grammarFile.lastModified() > scannerFile.lastModified())
}

hi blaine,

just a side note about the snippet above. you should replace snippets like

new File('src/com/admc/jcreole/CreoleParser.grammar')

with

file('src/com/admc/jcreole/CreoleParser.grammar')

as relative paths in “new File(…)” are depending on the working directory of your gradle build. especially in multi project builds this can easily cause trouble and misunderstandings. for details about file(…) have a look at org.gradle.api.Project:file(java.lang.Object)