How to use commandLine() within buildFinished - gradle

In $HOME/.gradle/init.gradle I have:
gradle.buildFinished { buildResult ->
commandLine 'bash', "blah blah blah"
}
Doing gradle build the build succeeds, but after it succeeds I get the error:
Could not find method commandLine() for arguments [bash, blah blah blah] on build 'FooBar' of type org.gradle.invocation.DefaultGradle.
The answer to the question Could not find method commandLine() doesn't help as either putting (type: Exec) after gradle.buildFinished or wrapping the whole thing in exec { } causes Gradle to fail right from the start rather than the build succeeding and then my post-build hook failing.
I'm using Gradle version 6.3

You've misconfigured commandLine. With the following build.gradle:
task lol {
doLast {
println "lol"
}
}
and ~/.gradle/init.gradle:
gradle.buildFinished { buildResult ->
exec {
commandLine 'bash', '-c', 'echo lol2'
}
}
it works as expected.

Related

How to find and replace lines in the build.gradle before executing it, using shell script

I have a build.gradle file which usually been executed in a windows environment; but when the same file is checked out in a Linux environment, the batch file mentioned in some of the lines gets conflicted, so what we were trying is to replace the exact lines where the build gets failed, to the lines that makes it work, using a shell script after the checkout gets completed.
build.gradle file
......
......
task exportBatch(type:Exec) {
doFirst {
println "Exporting batch..."
commandLine = ['cmd', '/C', 'start', 'export.bat']
}
}
task importBatch(type:Exec) {
doFirst {
println "Importing batch..."
commandLine = ['cmd', '/C', 'start', 'import.bat']
}
}
.....
.....
I would like to achieve the following before executing the build; after the source codes gets checked out to local workspace.
In task exportBatch, the line to be used is
commandLine 'sh', './export.sh'
instead of
commandLine = ['cmd', '/C', 'start', 'export.bat']
and
in task importBatch, the line to be used
commandLine 'sh', './import.sh'
instead of
commandLine = ['cmd', '/C', 'start', 'import.bat']
We tired changing the line based on the line number, but when there is a change of line numbers our method fails.
Using sed you can try
$ sed "/exportBatch/,/}/ {s|commandLine =.*|commandLine 'sh', './export.sh'|}; /importBatch/,/}/ {s|commandLine =.*|commandLine 'sh', './import.sh'|}" input_file
......
......
task exportBatch(type:Exec) {
doFirst {
println Exporting batch...
commandLine 'sh', './export.sh'
}
}
task importBatch(type:Exec) {
doFirst {
println Importing batch...
commandLine 'sh', './import.sh'
}
}
.....
.....
/exportBatch/,/}/ - Match between exportBatch and }
s|commandLine =.*|commandLine 'sh', './export.sh'| - Within the match above, substitute the line with content commandLine =.* with commandLine 'sh', './export.sh' NOTE Double quotation was used.
| - The pipe was used as a delimiter but any symbol that does not appear in the data can be used as the delimiter. As your data had /, the default sed delimiter could not be used as it would conflict and cause an error.
The same conditions are repeated in the second part of the code to handle the second substitution.

dependsOn another task with a parameter value

I have a task from a plugin which I call on command line like this:
$ gradle myTask -PsomeArg=<value>
I want to define another task that would do exactly the same with a default parameter value. I expect to be able to call it like this:
$ gradle otherTask
I assume my task should look something like this:
task otherTask (dependsOn: 'myTask', someArg: 'value') {...}
You could define a task that would set default value for project property someArg when this value is not provided as command line parameter :
task otherTask (){
finalizedBy myTask
doFirst{
if (!project.hasProperty("someArg")) {
project.ext.set("someArg", "defaultValue")
}
}
}
Note that you need to use finalizedBy dependency type here: if you use otherTask.dependsOn myTask the task myTask will be executed first (so default value for property someArg will not be set yet)
Execution result:
./gradlew myTask -PsomeArg=myValue
> Task :myTask
executing task pluginTask with arg: myValue
./gradlew otherTask -PsomeArg=myValue
> Task :myTask
executing task pluginTask with arg: myValue
./gradlew otherTask
> Task :myTask
executing task pluginTask with arg: defaultValue

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

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?
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.
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.

No such file or directory when adding arguments to Gradle exec task

I am making task which runs bash script
task wsUpload(type: Exec) {
commandLine '../scripts/ws_upload.sh ' + rootProject.ext.VERSION_CODE
}
however it returns
Caused by: java.io.IOException: Cannot run program "../scripts/ws_upload.sh 30" .... No such file or directory
if i ran same command without arguments
task wsUpload(type: Exec) {
commandLine '../scripts/ws_upload.sh'
}
then command is executed. What am i doing wrong?
Add args like this
task wsUpload(type: Exec) {
commandLine '../scripts/ws_upload.sh'
args = ["args"]
}

Purpose of << in gradle

I starting playing around gradle and trying to understand the purpose of <<.
On executing gradle -q task0 when build.gradle contains gave me task 0
4.times { counter ->
task "task$counter" <<{
println "task $counter"
}
}
where as executing gradle -q test{0123} when build.gradle contains
4.times { counter ->
task "task$counter" {
println "task $counter"
}
}
returning
task 0
task 1
task 2
task 3
Can someone help in understanding purpose of <<?
If you have << it will execute the print lines when the task is executed and not having it executes the print lines when the task is configured.
This can effect your output since the Gradle Lifecycle is Initialization > Configuration > Execution.
For example if your build.gradle contains:
task "task0" << {
println "task 0"
}
task "task1" {
println "task 1"
}
Then if you execute task0 you will get:
> gradlew -q task0
task 1
task 0
Even though you would only expect task 0 to print. While if you execute task1 you get:
> gradlew -q task1
task 1
This is because task1 is printing out during configuration which even though you are not running it, the whole build.gradle file gets read in during the configuration phase to determine what gets executed. You could even go:
> gradlew clean
task 1
And you get your output from task1.
I think the problem here is what << means. It is predefined shift left operator and with it we say to a Gradle task. "Gradle task, I want closure which is after << to be put as your last action".So with
task "taskHello" <<{
println "Hello Gradle action"
}
you say in my build I have a task, which name is taskHello and I want when excecute it with
gradle taskHello
To do action
{
println "Hello Gradle action"
}
<< is a syntax sugar short for doLast{}, so you can rewrite your task like this:
4.times { counter ->
task "task$counter" {
doLast{
println "task $counter"
}
}
}

Resources