Why does a type:Exec task require that one not use <<?

No inconsistency, ‘Exec’ tasks work the same as any other task you would have tried to configure. It’s all about how Gradle processes the build script.

When you run a build, Gradle goes through 3 phases. There’s a pretty good example in the 55.2 section of the Build Lifecycle chapter in the user guide.

Here’s another take on it:

task foo(type: Exec) {
    // this is configuration
    // i.e. it runs regardless
}
  task bar(type: Exec) << {
    // this is execution
    // i.e. it only runs if you call the task
}

Your task fails because ‘<<’ (i.e. ‘doLast’) adds an action that won’t be run until after the Gradle tries to execute the command specified by the ‘Exec’ task. Because you put the ‘commandLine ‘/bin/ls’’ in the ‘doLast’, Gradle wouldn’t know what command to run until after it tries to run it. Hence the failure.

The only times I find it makes sense to use ‘<<’ are on a one-off task:

task myfoo << {
  // do some stuff when this executes
}

Otherwise, for a typical task like ‘Exec’ you are almost certainly going to have to configure it first. If you have additional actions to run, you might as well just call ‘doLast’ later:

task foo(type: Exec) {
    commandLine '/bin/ls'
    doFirst {
       println '=== start foo ==='
    }
    doLast {
       println '=== end foo ==='
    }
}

Though you could still do this:

task foo(type: Exec) {
    commandLine '/bin/ls'
    doFirst {
       println '=== start foo ==='
    }
}
  foo << {
    println '=== end foo ==='
}
2 Likes