gradleincremental-build

Execute more than one command in a task without breaking incremental build


We use gradle incremental builds and want to run two commands (ideally from one task). One of the solutions here worked getting the two commands running... however it breaks incremental builds.. It looks something like:

task myTask() {
inputs.files(inputFiles)

  project.exec {
     workingDir web
     commandLine('mycmd')
  }
  project.exec {
    workingDir web
    commandLine('mysecond-cmd')
  }
}

if running a single command and incremental builds is working, the task looked similar to this, the thing that seems to make the difference is the workingDir:

task myTask(type: Exec)  {
     workingDir  myDir // this seems to trigger/enable continuos compilation
    commandLine ('myCmd')
 }

the best alternative so far is create 3 tasks, one for each of the cmdline tasks I want to run and a third one to group them, which seems dirty.

The question is: Is there a way to run two or more commands in one task with incremental builds still working?


Solution

  • I'll try to answer the question from the comments: how can I signal from a task that has no output files that the build should watch certain files. Unfortunately, this is hard to answer without knowing the exact use case.

    To start with, Gradle requires some form of declared output that it can check to see whether a task has run or whether it needs to run. Consider that the task may have failed during the previous run, but the input files haven't changed since then. Should Gradle run the task?

    If you have a task that doesn't have any outputs, that means you need to think about why that task should run or not in any given build execution. What's it doing if it's not creating or modifying files or interacting with another part of the system?

    Assuming that incremental build is the right solution — it may not be — Gradle does allow you to handle unusual scenarios via TaskOutputs.upToDateWhen(Action). Here's an example that has a straightforward task (gen) that generates a file that acts as an input for a task (runIt) with no outputs:

    task gen {
        ext.outputDir = "$buildDir/stuff"
        outputs.dir outputDir
    
        doLast {
            new File(outputDir, "test.txt").text = "Hurray!\n"
        }
    }
    
    task runIt {
        inputs.dir gen
        outputs.upToDateWhen { !gen.didWork }
        doLast {
            println "Running it"
        }
    }
    

    This specifies that runIt will only run if the input files have changed or the task gen actually did something. In this example, those two scenarios equate to the same thing. If gen runs, then the input files for runIt have changed.

    The key thing to understand is that the condition you provide to upToDateWhen() is evaluated during the execution phase of the build, not the configuration phase.

    Hope that helps, and if you need any clarification, let me know.