Synchronize directories with Gradle - 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.

Related

Gradle Copy task always UP-TO-DATE

I've seen this question posted numerous times, but I've not yet found an answer that matches my specific use case.
I'm created a bunch of config files from a template, after iterating over a config file containing environment properties.
The problem is that if I amend src/properties/ENV.gradle with some new values, it's ignored when I call the task on the basis that it's already "UP-TO-DATE". I'm not using doFirst or doLast, which is one of the common reasons this happens.
My workaround so far is to include
outputs.upToDateWhen { false }
which forces the config files to be recreated every single time regardless of what Gradle calculates, but it feels like an awful hack. Is there a better way?
Here's the code:
task createConfigs
def addTemplateTasks(aProject, env, config) {
aProject.with {
task "templateCopy_${env}"(type: Copy) {
from "src/templates"
include "server.config.template"
into "${buildDir}"
rename { file -> "server.config.${env}" }
expand(regionConfig)
}
createConfigs.dependsOn "templateCopy_${env}"
}
}
def envFile = new File("${project.projectDir}/src/properties/ENV.gradle")
def envConfig = new ConfigSlurper().parse(envFile.text)
envConfig.each { env, config ->
addTemplateTasks(project, env, config)
}
SOLUTION:
My case was a bit specific in that I was expecting the Copy task to notice "config" changing, without telling it explicitly care about it (see accepted answer below). Here's the change that fixed the problem:
def addTemplateTasks(aProject, env, config) {
aProject.with {
task "templateCopy_${env}"(type: Copy) {
inputs.property("environmentConfig", config)
The first arg (in quotes) is the name for this property, the second is the actual value.
Now, when I change a property in the ENV.gradle file and run "gradle -i", I see this:
Task ':component-foo:templateCopy_DEV' is not up-to-date because:
Value of input property 'environmentConfig' has changed for task ':component-foo:templateCopy_DEV'
The only input of your copy task, is the src/templates directory. Unless the content of src/templates doesn't change an the copied outputs are still present, your task will be considered UP-TO-DATE.
The problem is that your copy task actually has a second input which Gradle doesn't know about: Your config/regionConfig object.
Try to add the config object (or a hash of the objects values) to the inputs of the copy task using inputs.property or inputs.properties.

What is the equivalent of an Ant taskdef in 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.

Is it possible for a Gradle subsiduary script to use a task in a main script?

I've got a main build.gradle script which employs tasks in a secondary script.
If possible I'd like a task in the secondary script to depend upon one in the primary one. But it seems that's not possible. Or is it?
In a.gradle:
...
apply from: 'b.gradle'
task inA {
}
...
In b.gradle:
task inB {
dependsOn inA
}
This give:
A problem occurred evaluating script.
Could not find property 'inA' on task ':app:inB'.
Is this possible to do?
Build scripts are evaluated sequentially (top to bottom) so in this case task 'inA' doesn't exist yet when 'b.gradle' is applied. Simple fix is to use strings instead of symbols when referring to a task.
task inB {
dependsOn 'inA'
}

How do I force a reconfiguration of projects in Gradle?

I have a build.gradle file that calls some SVNKit stuff to svn export some directories that make up a Gradle multi-project.
I have a task dedicated to doing this that looks something like this:
task checkoutIntoDir() << {
mkdir 'dirForSvnProjects_2014_07_17_19_50' // timestamp not hard-coded ;)
// prompt for username/password
// run svn export which places projects in dirForSvnProjects_2014_07_17_19_50
}
with another GradleBuild task that depends on it:
task buildCheckedOutStuff(type: GradleBuild, dependsOn: checkoutIntoDir) {
dir = "dirForSvnProjects_2014_07_17_19_50/svnProjectIExported"
tasks = ['buildMyProj']
}
But it says task 'buildMyProj' not found in root project when it gets there. Now if I take out the task dependency checkoutIntoDir and run it on a directory that's there before I start the build, it works fine. I'm guessing I need to run some kind of "reconfiguration" to make the project aware of the new gradle project in dirForSvnProjects_2014_07_17_19_50?
I finally figured it out.
It was actually related to the way I was setting variables, not about Gradle.
I had a createDir task defined that set a variable that was defined (but not set) at the "root" level of the script. It looked something like this:
def myBuildDir
task createDir << {
def nowDate = String.format('%tY_%<tm_%<td_%<tH_%<tM',Calendar.instance)
myBuildDir= "_build_$nowDate"
mkdir myBuildDir
}
task checkoutIntoDir(dependsOn: createDir) << {
// prompt for username/password
// run svn export which places projects in myBuildDir
}
And as Perryn Fowler pointed out in the comments, any block that's not in a doLast (or the << shorthand)-type block will be run at configuration time when createDir was being run at runtime. Therefore the variable was not set for the buildCheckedOutStuff task.
So I just changed it to set the date at the root as well and it worked:
def nowDate = String.format('%tY_%<tm_%<td_%<tH_%<tM',Calendar.instance)
def eqipBuildDir = "_build_$nowDate"
task createDir << {
mkdir eqipBuildDir
}
That's what I get for leaving out pieces for brevity!

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

Resources