task originalInputs() {
    doLast {
        file('inputs').mkdir()
        file('inputs/1.txt').text = 'Content for file 1.'
        file('inputs/2.txt').text = 'Content for file 2.'
        file('inputs/3.txt').text = 'Content for file 3.'
    }
}

task updateInputs() {
    doLast {
        file('inputs/1.txt').text = 'Changed content for existing file 1.'
        file('inputs/4.txt').text = 'Content for new file 4.'
    }
}

task removeInput() {
    doLast {
        file('inputs/3.txt').delete()
    }
}

task removeOutput() {
    doLast {
        file("$buildDir/outputs/1.txt").delete()
    }
}

task incrementalReverse(type: IncrementalReverseTask) {
    inputDir = file('inputs')
    outputDir = file("$buildDir/outputs")
    inputProperty = project.properties['taskInputProperty'] ?: 'original'
}

class IncrementalReverseTask extends DefaultTask {
    @InputDirectory
    def File inputDir

    @OutputDirectory
    def File outputDir

    @Input
    def inputProperty

    @TaskAction
    void execute(IncrementalTaskInputs inputs) {
        println inputs.incremental ? 'CHANGED inputs considered out of date'
                                   : 'ALL inputs considered out of date'
        if (!inputs.incremental)
            project.delete(outputDir.listFiles())

        inputs.outOfDate { change ->
            println "out of date: ${change.file.name}"
            def targetFile = new File(outputDir, change.file.name)
            targetFile.text = change.file.text.reverse()
        }

        inputs.removed { change ->
            println "removed: ${change.file.name}"
            def targetFile = new File(outputDir, change.file.name)
            targetFile.delete()
        }
    }
}
