0
votes

I have a gradle project with the following structure:

I have a sequence of tasks (let's call them taskA, taskB, taskC), that take one input file (let's call it source.tar.gz) and generates an output file (let's call it dist.tar.gz). Assume taskA/taskB/taskC will need to run processes, etc.

I also have a task (called taskZ) that makes sure to cleanup after taskA, taskB, taskC. Assume it will stop processes started by the first set of tasks.

I have a custom task called 'make' that depends on the build and its finalized. Something like this:

task make {
    dependsOn taskA, taskB, taskC
    finalizedBy taskZ
}

Finally, gradle's built in 'assemble' task depends on my custom 'make' task.

I would like to let gradle know that make's input is 'source.tar.gz' file and output is 'dist.tar.gz' so 'make' doesn't need to run if 'dist.tar.gz' is newer than 'source.tar.gz'.

I tried declaring 'make' input and outputs, like this:

task make {
    dependsOn taskA, taskB, taskC
   finalizedBy taskZ

   inputs.file("$projectDir/src/main/source.tar.gz")
   outputs.file("$buildDir/dist.tar.gz")
}

But gradle is still running the whole process everytime.

Is there any way I can tell gradle to skip 'make' task and its dependencies?

2
make will be up to date if taskA, taskB and taskC are up to date. So the question is: why are those not up to date in this situation? - Peter Ledbrook
let's assume taskA expands the tar file, taskB starts a server process, taskC makes a call to the server process to do execute an action, taskZ terminates the server process. In that case taskA may be up to date but starting a process and killing the process will run always. That's why I tried to have a separate task that calls all of them. - Ramon Rivas
This leads to a similar problem as in Only run task if another isn't UP-TO-DATE in gradle and would require a feature currently discussed under this GitHub issue. - Lukas Körfer
@lu.koerfer thanks. Your suggestion is what helped me solve the problem. - Ramon Rivas

2 Answers

1
votes

Gradle's incremental build is not solely based on timestamps (timestamps may not even be considered these days), so it won't help with what you're trying to do. On top of that, I'm pretty sure tasks A -> C will always run even with make configured that way. make must first ensure that its dependencies are run or already up to date.

Given this, I think you may need to use a dedicated timestamp check in an onlyIf() check. Something like:

make.onlyIf { inputFile.lastModified() > outputFile.lastModified() }

where inputFile and outputFile are paths to the relevant files (using project.file() for example). I don't think this is terribly reliable, but it may suffice for your use case.

Alternatively, create a custom task that performs B and C together, if you can configure such a task with appropriate defined inputs and outputs. I think this is the better solution based on the information given.

0
votes

I solved my problem following the suggestion from @lu.koerfer. I set tasks taskA, taskB, taskC, make, taskZ to have the same inputs and outputs. An I also marked taskB, taskC, taskZ to only run if taskA was not UP-TO-DATE.

With that configuration, taskA will only run if gradle uses its own mechanism to execute taskA, and only if taskA is not marked as UP-TO-DATE by gradle, then the rest of the tasks will run.

task taskA {
    ...
}

task taskB {
    ...
    onlyIf { !taskA.state.upToDate }
}

task taskC {
    ...
    onlyIf { !taskA.state.upToDate }
}

task taskZ {
    ...
    onlyIf { !taskA.state.upToDate }
}

task make {
    dependsOn taskA, taskB, taskC
    finalizedBy taskZ
    ...
}

[taskA, taskB, taskC, taskZ, make].each { t ->
   t.inputs.file("$projectDir/src/main/source.tar.gz")
   t.outputs.file("$buildDir/dist.tar.gz")
}

This works as expected. Sometimes gradle up to date mechanism may not work. If you need to force-run all tasks, just run:

./gradlew make --rerun-tasks