What is the equivalent of an Ant taskdef in Gradle? - gradle

I've been struggling with this for a day and a half or so. I'm trying to replicate the following Ant concept in Gradle:
<target name="test">
...
<runexe name="<filename> params="<params>" />
...
</target>
where runexe is declared elsewhere as
<macrodef name="runexe" >
...
</macrodef>
and might also be a taskdef or a scriptdef i.e. I'd like to be able to call a reusable, pre-defined block of code and pass it the necessary parameters from within Gradle tasks. I've tried many things. I can create a task that runs the exe without any trouble:
task runexe(type: Exec){
commandLine 'cmd', '/c', 'dir', '/B'
}
task test(dependsOn: 'runexe') {
runexe {
commandLine 'cmd', '/c', 'dir', '/N', 'e:\\utilities\\'
}
}
test << {
println "Testing..."
// I want to call runexe here.
...
}
and use dependsOn to have it run. However this doesn't allow me to run runexe precisely when I need to. I've experimented extensively with executable, args and commandLine. I've played around with exec and tried several different variations found here and around the 'net. I've also been working with the free books available from the Gradle site.
What I need to do is read a list of files from a directory and pass each file to the application with some other arguments. The list of files won't be known until execution time i.e. until the script reads them, the list can vary and the call needs to be made repeatedly.
My best option currently appears to be what I found here, which may be fine, but it just seems that there should be a better way. I understand that tasks are meant to be called once and that you can't call a task from within another task or pass one parameters but I'd dearly like to know what the correct approach to this is in Gradle. I'm hoping that one of the Gradle designers might be kind enough to enlighten me as this is a question asked frequently all over the web and I'm yet to find a clear answer or a solution that I can make work.

If your task needs to read file names, then I suggest to use the provided API instead of executing commands. Also using exec will make it OS specific, therefore not necessarily portable on different OS.
Here's how to do it:
task hello {
doLast {
def tree = fileTree(dir: '/tmp/test/txt')
def array = []
tree.each {
array << it
print "${it.getName()} added to array!\n"
}
}
}

I ultimately went with this, mentioned above. I have exec {} working well in several places and it seems to be the best option for this use case.
To please an overzealous moderator, that means this:
def doMyThing(String target) {
exec {
executable "something.sh"
args "-t", target
}
}
as mentioned above. This provides the same ultimate functionality.

Related

How do I remove/overwrite the outputs from a gradle task

I have dynamic defined a task in build.gradle, that inherits from another task.
In the following I'm using kotlin-DSL, but I'm happy with solutions for groovy dsl as well.
task.register<GenerateTask>("generateCode") {
// stuff
outputDir.set("$rootDir")
}
As you can see I had to define the output as rootDir.
However I'm setting options to only create part of the output.
All the generated code is in a specificPackage meaning it will all end up in:
$rootDir/src/main/my/generated/package/name
$rootDir/src/test/my/generated/package/name
However due to outputDir being set it assumes the whole rootdir is an output and warns me accordingly.
Now I would like to tell gradle, that this task only produces those two packages.
I tried overwriting outputs but it is readonly.
I tried overwriting outputs.files but it is readonly.
I tried using outputs.files.setFrom() as described here, but this seems no longer to be valid.
Is there a way to set outputDir, but not add the value it to outputs or a way to clear the outputs list?

Why are optional inputs on my Gradle custom task not working?

I have a build.gradle with the following contents:
task myTask {
inputs.file("input.txt").optional()
doLast { println "input.txt exists = " + file("input.txt").exists() }
}
If input.txt doesn't exist, it fails with:
File '/Users/skissane/testgradle/input.txt' specified for property '$1' does not exist.
What I am trying to do, is run a custom script–which is written in Groovy, and runs inside the Gradle build under doLast, not as an external process–which takes the input.txt file as input, and the script's behaviour and output will change based on what is in that input file. But it is an optional input file – the script will still generate output (albeit different output) even if the input file doesn't exist.
Things I have tried so far:
Remove .optional(), change it to .optional(true): no difference in results
Instead of .optional(), wrap it in if (file("input.txt").exists()) {: this works, but seems ugly. Why doesn't .optional() work?
Have I misunderstood what .optional() is meant to do? Because another answer suggests it is the right way to solve my problem, but it isn't working.
(I am using Gradle 6.8.3. I tried upgrading to the latest Gradle 7.2, the same problem occurs, although 7.2 has more detailed error messages.)
optional() can't be used to mark the file itself as optional. optional() just means that the input property is optional, and the task is still valid if no files at all are specified; but if a file is specified, it must exist.
As such, optional() isn't really useful in this kind of custom task declared directly in build.gradle. It is really intended for defining new task types in plugins, when one defines a new task input property other than inputs, and wants to make it optional to declare files for that property. It is the property itself which is made optional, not the files in it. On a custom task, declaring inputs as optional is pointless because it is already optional to begin with.
Right now (as of version 7.2), Gradle doesn't have any way to mark a file as an optional input, other than through if (file("input.txt").exists()) {. Hopefully they might add that feature in some future Gradle version.
(Thanks to James Justinic who answered my post about this on Gradle forums.)

Synchronize directories with Gradle

How can I synchronize two directories after task execution? I can do it like this:
task syncDirs(type: Sync) {
...
}
task someTask {
doLast {
syncDirs.execute()
}
}
But method "execute" is internal and I have to avoid it.
Thanks for the answer in advance.
Depending on your exact needs, you can use syncDirs.dependsOn(someTask), or call the delete and copy methods inside someTask.doLast (that's how Sync is currently implemented).
task myTask << {
copy {
from 'src_dir'
into 'dst_dir'
include 'myfile.txt'
}
sync {
from "src_dir/foo"
into "dst_dir/bar"
}
}
In Gradle 1.10, you can do things like copy files and sync dirs all in a single task. I prefer this to having separate tasks for copying and syncing.
For syncing, the idea of doing a separate delete and copy, as suggested above, seems tedious. I'm glad I can call sync to do both.

Task based on Copy appears not to execute()

I have the following code in my build.gradle file:
task untarServer(type:Copy) {
from tarTree('build/libs/server.tar.gz')
into project.ext.tomcat + '../'
} << {
println 'Unpacked, waiting for tomcat to deploy wars ...'
sleep 10000
}
task deploy << {
tarball.execute()
untarServer.execute()
stopTomcat.execute()
startTomcat.execute()
}
Everything works great except untarServer, which appears not to run at all. Untar is based on an example from the documentation. Probably I've done something silly due to the late hour, but I'm missing it. How can I fix this? I have in the back of my mind that I could drop down to using ant.untar, but I'd like to do it the native gradle way if possible.
Edit: How do I know it's not running? because the println statement never shows up, the buld does not pause for 10 seconds, and the contents of the tarball do not show up in the "into" location.
I haven't seen the } << { syntax before; I'd change it to doLast { ... } inside the curly braces or untarServer << { (as a separate statement). Also, you should never call execute() on a task. It's totally unsupported and bad things will happen. Instead you should establish a task relationship (dependsOn, mustRunAfter, finalizedBy).

Controlling Gradle task execution

In my build.gradle script, I have a lot of tasks, each depending on zero or more other tasks.
There are three 'main' tasks which can be called: moduleInstallation, backupFiles and restoreFiles.
Here's the question: I would like to be able to tell Gradle which tasks to execute and which don't need to execute. For example, when calling moduleInstallation, I want all depending tasks to execute (regardless of their UP-TO-DATE flag), but not the restore tasks. I've tried altering the phase in which the tasks get executed (e.g. config phase, execution phase,...) and a couple of other things, but all tasks just keep getting executed.
A solution I've thought of was just stating in the main tasks that, when this main task is called (f.e. moduleInstallation), we set the UP-TO-DATE flag of all non-related tasks to false, so they don't get executed. Is that possible?
EDIT: Here's an example:
When moduleInstallation is called (which depends on backupFiles), restoreFiles (which depends on restoreFromDate) is executed too.
First main action
task moduleInstallation << {
println "Hello from moduleInstallation"
}
task backupFiles {
doLast {
println "Hello from backupFiles"
}
}
Second main action
task restoreFiles {
println "Hello from restoreFiles"
}
task restoreFromDate {
println "Hello from restoreFromDate"
}
Dependencies:
moduleInstallation.dependsOn backupFiles
restoreFiles.dependsOn restoreFromDate
So when I type gradle moduleInstallation in the terminal, I get the following output:
Hello from restoreFromDate
Hello from restoreFiles
Hello from backupFiles
Hello from moduleInstallation
The second snippet has to use doLast (or its << shortcut) like the first snippet. Otherwise, the code is configuration code and will always be evaluated, no matter which tasks are eventually going to be executed. In other words, it's not the restoreFiles and restoreFromDate tasks that are being executed here (as one can tell from the bits of command line output that you don't show), but (only) their configuration code.
To better understand what's going on here (which is crucial for understanding Gradle), I recommend to study the Build Lifecycle chapter in the Gradle User Guide.

Resources