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

noob warning here. I’m sure I’m missing some fundamental bit of lore.

I have an exec task:

task buildFoo(type:Exec) {
 logger.lifecycle('== Building Foo ==')
        ....

I always see “== Building Foo ==” in the output even if I’m building a different target.

If I change to

task buildFoo(type:Exec) << {
 logger.lifecycle('== Building Foo ==')
        ....

Then I no longer see the “== Building Foo ==” in other targets, but the exec target is broken:

* What went wrong:
Execution failed for task ':buildToc'.
> execCommand == null!
1 Like

With no ‘<<’, you are running that closure in the configuration phase of Gradle. This means that it runs everytime you do a build, regardles of the tasks you execute.

With the ‘<<’, you are adding a ‘doLast’ action which would run as the last thing the task does (i.e. after it runs the command specified on the ‘Exec’ task).

The failure seems suspicious to me (given the ‘buildToc’ name vs. the ‘buildFoo’ name). If those are really separate tasks the ‘<<’ on ‘buildFoo’ should have absolutely no effect.

My fault on the task names. I tried to make everything generic and missed the last Toc->Foo change.

Here’s a completely clean example, like I should have done the first time:

task notfoo << {
        println '== notfoo =='
}
task foo(type:Exec) {
        println '== foo =='
        workingDir '.'
        commandLine '/bin/ls'
        standardOutput = new ByteArrayOutputStream()
}
foo.doLast {
        println '== foo last =='
        println standardOutput
}

The output:

~/tmp %> gradle foo == foo == :foo == foo last == build.gradle

BUILD SUCCESSFUL

Total time: 1.634 secs

~/tmp %> gradle notfoo == foo == :notfoo == notfoo ==

BUILD SUCCESSFUL

Total time: 0.985 secs

If I change the script to use << (the only change)

task foo(type:Exec) << {

Now I see that foo fails, but when building notfoo it is no longer printing out == foo ==

~/tmp %> gradle foo :foo FAILED

FAILURE: Build failed with an exception.

  • What went wrong: Execution failed for task ‘:foo’. > execCommand == null!

  • Try: Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

BUILD FAILED

Total time: 0.998 secs

~/tmp %> gradle notfoo :notfoo == notfoo ==

BUILD SUCCESSFUL

Total time: 0.981 secs

The real question is whether the foo task exec command runs when building notfoo, or is everything except the actual exec run?

It feels like thers’s an inconsistency in the use of << for exec tasks.

Is it that the exec task body is just setup and nothing actually runs until later ? In which, case, when does the exec run, when the doLast is invoked?

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

Andrew, Thanks for the link & excellent explanation. Always happy to RTFM if I can find the right page in the FM :slight_smile:

Hi,

How to run a windows command using command prompt? Any pointers or suggestions are welcome.

Regards, Sam

Hi Andrew,

I am facing another issue with exec tasks.

My build.gradle looks like this

project(‘:MY.lib’) {

task removePrevFiles(type: Exec) {

println “Removing existing cpp and hpp files.”

commandLine ‘${rmCmd}’

}

task generateCPPStubs(type: Exec) {

println “Now generating cpp and hpp stub files”

dependsOn removePrevFiles

workingDir ‘.’

commandLine ‘${Cmd}’

standardOutput = new ByteArrayOutputStream()

}

libraries {

MYLib { baseName ‘Lib1’ }

}

sources {

MYlib {

generateCPPStubsOutput(CppSourceSet) {

generatedBy tasks.generateCPPStubs

}

}

}

}

but this is giving exception like below

A problem occurred configuring project ‘:MY.lib’. > Exception thrown while executing model rule: org.gradle.nativebinaries.plugins.NativeComponentModelPlugin$Rules#configureGeneratedSourceSets(org.gradle.language.base.ProjectSourceSet)

Could not find property ‘sourceDir’ on task ‘:MY.lib:generateCPPStubs’.

Could you please give any pointer what is missing here