Lai Yu-Hsuan
Lai Yu-Hsuan

Reputation: 28121

Create makefile-like wildcard targets in Gradle

Use case: I have a bunch of images that have to be processed by a script before I build my app. In makefile I can simply define:

processed/%.png: original/%.png
   script/process.sh $< $@

How do I implement this in Gradle? Specifically, I want it to work like in Makefile, that is only the modified original images will be processed again.

Upvotes: 2

Views: 234

Answers (1)

MartinTeeVarga
MartinTeeVarga

Reputation: 10898

You can implement this behaviour as an incremental task, using IncrementalTaskInputs as its input parameter. This API docs contain an example how to use it and here is an example in another the documentation. Both of them do almost exactly what you need.

An incremental task action is one that accepts a single IncrementalTaskInputs parameter. The task can then provide an action to execute for all input files that are out of date with respect to the previous execution of the task, and a separate action for all input files that have been removed since the previous execution.

In the case where Gradle is unable to determine which input files need to be reprocessed, then all of the input files will be reported as IncrementalTaskInputs.outOfDate(org.gradle.api.Action).

Inside your task, call the script using an exec task. Your Gradle script could then look like this:

task processRawFiles(type: ProcessRawFiles)

class ProcessRawFiles extends DefaultTask {
    @InputDirectory
    File inputDir = project.file('src/raw')

    @OutputDirectory
    File outputDir = project.file('build/processed')

    @TaskAction
    void execute(IncrementalTaskInputs inputs) {
        if (!inputs.incremental)
            project.delete(outputDir.listFiles())

        inputs.outOfDate { InputFileDetails change ->
            File saveTo = new File(outputDir, change.file.name)
            project.exec {
                commandLine 'script/process.sh', change.file.absolutePath, saveTo.absolutePath
            }
        }

        inputs.removed { InputFileDetails change ->
            File toDelete = new File(outputDir, change.file.name)
            if (toDelete.exists())
                toDelete.delete()
        }
    }
}

This task looks for the images in src/raw. It will removed files from build directory and call your script on any files that are out of date or newly added.

Your specific case might be more complicated if you have the images scattered across multiple directories. In that case you will have to use @InputFiles instead of @InputDirectory. But the incremental task should still work.

Upvotes: 1

Related Questions