Unable to create a Gradle task rule with an Exec task - gradle

I'm trying to write a task rule to create a series of tasks that checkout various svn repository locations. Here is my rule:
tasks.addRule("Pattern: svnCheckout&ltClassifier> - Checks out the indicated svn repo") { String taskName ->
if(taskName.startsWith('svnCheckout')) {
task(name: taskName, type: Exec) {
String classifier = taskName - 'svnCheckout'
String svnDir = svnRepoUrl //defined elsewhere
switch(classifier) {
case 'SourceTrunk':
svnDir += 'branches/CleanBuild/trunk'
break
case 'AutoInstaller':
svnDir += 'Tools/AutoInstaller'
break
case 'ContentAutomation':
svnDir += 'Tools/ContentAutomation'
break
case 'InternalTools':
svnDir += 'Tools/Internal'
break
default:
throw new GradleException("Invalid svnCheckout classifier '$classifier'")
}
String svnCommand = "svn co $svnDir --trust-server-cert"
//commandLine 'cmd', '/c', "$svnCommand"
commandLine 'cmd', '/c/', "echo 'Task created'"
workingDir = "$workspace"
}
}
}
I then try to run the task 'svnCheckoutSourceTrunk' with this command:
gradlew -Pworkspace="." svnCheckoutSourceTrunk
which fails with the error
FAILURE: Could not determine which tasks to execute.
* What went wrong:
Task 'svnCheckoutSourceTrunk' not found in root project 'GradleScripts'.
* Try:
Run gradlew tasks to get a list of available tasks.
BUILD FAILED
Anyone see what I'm doing wrong? I put some println statements around the first few lines, and the execution is getting past the if statement, but it's not getting inside the task declaration.

The syntax used for declaring the task(s) is incorrect. (Not sure why it's not giving an error.) The first positional argument always need to be the task name:
task(taskName, type: Exec) { ... }
In a build script, this will also work:
task "$taskName"(type: Exec) { ... }

Related

how to play audio in gradle.kts? [duplicate]

I have a gradle build setup at the beginning of which I want to execute a shellscript in a subdirectory that prepares my environment.
task build << {
}
task preBuild << {
println 'do prebuild stuff:'
}
task myPrebuildTask(type: Exec) {
workingDir "$projectDir/mySubDir"
commandLine './myScript.sh'
}
build.dependsOn preBuild
preBuild.dependsOn myPrebuildTask
However, when I execute the task either by calling gradle myPrebuildTask or by simply building the project, the following error occurs:
> A problem occurred starting process 'command './myScript.sh''
Unfortunately, thats all I get.
I have also tried the following - same error.
commandLine 'sh mySubDir/myScript.sh'
I use Gradle 1.10 (needed by Android) on Windows, inside a Cygwin shell. Any ideas?
use
commandLine 'sh', './myScript.sh'
your script itself is not a program itself, that's why you have to declare 'sh' as the program and the path to your script as an argument.
A more generic way of writing the exec task, but portable for Windows/Linux, if you are invoking a command file on the PATH:
task myPrebuildTask(type: Exec) {
workingDir "$projectDir/mySubDir"
if (System.getProperty('os.name').toLowerCase(Locale.ROOT).contains('windows')) {
commandLine 'cmd', '/c', 'mycommand'
} else {
commandLine 'sh', '-c', 'mycommand'
}
}
This doesn't directly address the use case for the OP (since there is script file in the working directory), but the title of the question is more generic (and drew me here), so it could help someone maybe.
unfortunately options with commandLine not worked for me in any way and my friend find other way with executable
executable "./myScript.sh"
and full task would be
task startScript() {
doLast {
exec {
executable "./myScript.sh"
}
}
}
This works for me in my Android project
preBuild.doFirst {
println("Executing myScript")
def proc = "mySubDir/myScript.sh".execute()
proc.waitForProcessOutput(System.out, System.err)
}
See here for explanation:
How to make System command calls in Java/Groovy?
This is a solution for Kotlin DSL (build.gradle.kts) derived from Charlie Lee's answer:
task<Exec>("MyTask") {
doLast {
commandLine("git")
.args("rev-parse", "--verify", "--short", "HEAD")
.workingDir(rootProject.projectDir)
}
}
Another approach using the Java standard ProcessBuilder API:
tasks.create("MyTask") {
val command = "git rev-parse --verify --short HEAD"
doLast {
val process = ProcessBuilder()
.command(command.split(" "))
.directory(rootProject.projectDir)
.redirectOutput(Redirect.INHERIT)
.redirectError(Redirect.INHERIT)
.start()
process.waitFor(60, TimeUnit.SECONDS)
val result = process.inputStream.bufferedReader().readText()
println(result)
}
}
For more information see:
How to run a command line command with Kotlin DSL in Gradle 6.1.1?
How to invoke external command from within Kotlin code?
for kotlin gradle you can use
Runtime.getRuntime().exec("./my_script.sh")
I copied my shell scipt to /usr/local/bin with +x permission and used it as just another command:
commandLine 'my_script.sh'

How to sequential execute gradle task with sh scripts inside

I have 2 gradle task, one depends on the other.
The first task get db dump from remote server using sh script. The second task parse dump from previous task.
The problem is that second task starts early than dump script from the first task finishes. So there is no file yet, when second task start to work.
Tasks:
task getDumpFromRemotePostgres(type: Exec) {
executable = "/bin/sh"
println 'started task getDumpFromRemotePostgres'
def dumpScript = './createSqliteDb/scripts/dump_db_script.sh'
def dbDumpFileName = 'dump.sql'
args += [dumpScript]
println 'finished task getDumpFromRemotePostgres'
}
task patchPostgresDumpFile(type: Exec) {
dependsOn getDumpFromRemotePostgres
executable = "/bin/sh"
println 'started task patchPostgresDumpFile'
def dbDumpFileName = 'dump.sql'
File dumpFile = file(dbDumpFileName)
def line
dumpFile.withReader { reader ->
while ((line = reader.readLine()) != null) {
//parse and modify
}
println 'finished task patchPostgresDumpFile'
}
Script dump_db_script.sh:
echo "dump script started"
pg_dump --data-only --inserts --dbname=postgresql://user:pas#server/base_name > dump.sql
echo "dump script finished"
Console log is the follow (if i delete access file lines):
started task getDumpFromRemotePostgres
finished task getDumpFromRemotePostgres
started task patchPostgresDumpFile
finished task patchPostgresDumpFile
> Task :getDumpFromRemotePostgres
dump script started
dump script finished
Is any idea how to solve the problem?
Tried with doLast{ ... } but came to nothing
It seems, it's all due to the different lifecycle phases. You can read more about it here.
First of all, when you createe a task of type Exec or any kind without <<, everything in it's body is a task configuration and is getting executed at the configuration pahse of the build. That's why you have messages like started task getDumpFromRemotePostgres first in your output.
The second thing is that an executable you are running, is executed at the execution phase, after all task's configuration is done already. And that is why dump script started appears after all the configurations are done.
In your case, you don't need to declare a patchPostgresDumpFile as an Exec task, because you don't actually call any executable, but need to run some logic. For that, you have to move this logic to the doLast closure, to run it in the execution phase. Something like this:
task patchPostgresDumpFile() {
dependsOn getDumpFromRemotePostgres
doLast {
println 'started task patchPostgresDumpFile'
def dbDumpFileName = 'dump.sql'
File dumpFile = file(dbDumpFileName)
def line
dumpFile.withReader { reader ->
while ((line = reader.readLine()) != null) {
//parse and modify
}
println 'finished task patchPostgresDumpFile'
}
}
And note, that your message started task getDumpFromRemotePostgres doesn't actually mean that this task is running, but that it's configuring. If you want to have a messages before and after the execution, move them into the doFirst and doLast closures within a task configuration closure.

How to specify in Gradle an `#OutputDirectory` for an `Exec` task?

I have the following:
task cloneProtobuf(type: Exec) {
workingDir "${rootProject.buildDir}/github.com/google"
commandLine 'git', 'clone', 'https://github.com/google/protobuf.git'
enabled = { -> !new File(workingDir, "protobuf/.git").isDirectory() }()
doFirst {
mkdir workingDir
}
}
Rather than explicitly setting enabled and having gradle indicate that the task was SKIPPED, I would rather have gradle consider that if the protobuf directory already exists, the task is UP-TO-DATE. How can this be done?
Instead of enable/disable a task, please register a task output, then gradle will be aware itself if a task is up-to-date or not. Please have a look at the example below which you can find helpful:
task mk(type: Exec) {
def output = project.file('mk')
outputs.dir output
commandLine 'mkdir', output
workingDir '.'
}
If you run gradle mk twice, the task will execute only once.

Gradle clean erasing my file prior to ZIP task execution

I have the following simple task in my build:
task generateFile << {
def file = new File("$buildDir/setclasspath.sh")
file.text = "sample"
outputs.file(file)
}
task createDistro(type: Zip, dependsOn: ['copyDependencies','packageEnvironments','jar', 'generateFile']) <<{
from generateClasspathScript {
fileMode = 0755
into 'bin'
}
}
When I run gradle clean build I see the following output:
Cannot call TaskOutputs.file(Object) on task ':generateFile' after task has started execution. Check the configuration of task ':generateFile' as you may have misused '<<' at task declaration
How do I declare the task file creation outputs as an input to the zip task while also ensuring they happen in the execution phase?
If I leave off the << then the clean task wipes the generated file before the ZIP can use it. If I keep them, I get the above error.
It's the opposite as what is being suggested in the comments. You are trying to set the outputs in execution phase. The correct way to do what you are probably trying to do is for example:
task generateFile {
def file = new File("$buildDir/setclasspath.sh")
outputs.file(file)
doLast {
file.text = "sample"
}
}

Gradle executes all tasks?

I have a very simple build script like so
task hello{
println("hello World")
}
task bye {
println("bye")
}
On the command line I run
gradle hello and I get the following output:
hello World
bye
:hello UP-TO-DATE
Why is it executing the task "bye" (I'm assuming it gets executed since "bye" gets printed)? Thanks.
It's a common pitfall:
task hello {
println("Any code in here is about *configuring* the\
task. By default, all tasks always get configured.")
doLast {
println("Any code in here is about *executing* the task.\
This code only gets run if and when Gradle decides to execute the task.")
}
}
The distinction between configuration phase and execution phase is probably the single most important concept to understand in Gradle. It can be confusing at first, and may go away in the future. A kind of analogue in the Ant/Maven world is that these tools first parse XML build scripts and build an object model (perhaps resolving some properties along the way), and only then execute the build.
Adding to Peter answer, If you want to execute all task , you can specify the defaultTasks list.
defaultTasks 'clean', 'run'
task clean {
doLast {
println 'Default Cleaning!'
}
}
task run {
doLast {
println 'Default Running!'
}
}
task other {
doLast {
println "I'm not a default task!"
}
}
Output
Output of gradle -q
> gradle -q
Default Cleaning!
Default Running!
More details can be found here
https://docs.gradle.org/current/userguide/tutorial_using_tasks.html

Resources