Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Gradle exec task fails with "execCommand == null!"

Tags:

gradle

I have the following task in my build.gradle file:

task myTask(type:Exec) {
    def stdout = new ByteArrayOutputStream()
    exec {
        commandLine 'cmd', '/c', 'whoami'
        standardOutput = stdout;
    }
    println "Output: $stdout"
}

When I run my task with ./gradlew myTask, I get the following output:

> Configure project :
Output: retrovius


> Task :myTask FAILED

FAILURE: Build failed with an exception.

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

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 2s
1 actionable task: 1 executed

The task successfully outputs my username (retrovius), then fails anyway. Any pointers for what I'm doing wrong?

like image 617
retrovius Avatar asked Oct 24 '19 13:10

retrovius


Video Answer


2 Answers

Depending on what you want to achieve, the answer you found is probably still not correct.

All tasks have two main stages: configuration and execution. Everything you put in the outermost block to the task definition is part of the configuration. And the exec method actually executes the command whenever that block of code is evaluated. So when you type:

task myTask() {
    def stdout = new ByteArrayOutputStream()
    exec {
        commandLine 'cmd', '/c', 'whoami'
        standardOutput = stdout;
    }
    println "Output: $stdout"
}

Then it means you are running the whoami command no matter what task you specify. If you run gradle -i help, it will print the name. I expect this is not what you intend.

Most of the time, you will want to run a command only when the task is actually executed. So if you want the command to only run if you type gradle -i myTask, you will need to do defer it to the execution stage instead. There are two ways you can do that.

Either you can put everything in a doLast block like this:

task myTask() {
    doLast {
        def stdout = new ByteArrayOutputStream()
        exec {
            commandLine 'cmd', '/c', 'whoami'
            standardOutput = stdout
        }
        println "Output: $stdout"
    }
}

Or you use the Exec type, like you already tried. The reason it didn't work for you is that you need to configure it with the command you like - and not actually run the command through the exec method. It could look like this:

task myTask(type: Exec) {
    commandLine 'cmd', '/c', 'whoami'
    standardOutput = new ByteArrayOutputStream()
    doLast {
        println "Output: $standardOutput"
    }
}

You an also probably get rid of the cmd /c part. And println should only be used for debugging - use logger.info (or .warn, etc) if you need to output something to the user.

like image 188
Bjørn Vester Avatar answered Oct 19 '22 10:10

Bjørn Vester


I figured out that the only thing I was doing wrong was to include the (type:Exec) in the definition of my task. If I place the following code in my build.gradle file:

task myTask() {
    def stdout = new ByteArrayOutputStream()
    exec {
        commandLine 'cmd', '/c', 'whoami'
        standardOutput = stdout;
    }
    println "Output: $stdout"
}

I get the following output:

> Configure project :
Output: retrovius


BUILD SUCCESSFUL in 2s

My mistake must have been that I was defining the task to be of type exec, but not giving it a command to run. This reveals a fundamental misunderstanding of the exec task and task type on my part. If anyone knows more specifically what I did wrong, please feel free to comment and explain or post a better answer.

like image 33
retrovius Avatar answered Oct 19 '22 12:10

retrovius