How do I force a reconfiguration of projects in Gradle? - 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!

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.

Copy task in Gradle says "UP TO DATE" on first attempt

I have a copy task that is trying to copy files extracted in the 'dependsOn' task. The destination folder contains a timestamp which is retrieved from a properties file.
task copyFilesToBuild(type: Copy, dependsOn:unzipExtFile) {
def tmpTimestampFile = file("$buildDir/tmp/timestamp.properties")
if (tmpTimestampFile.exists()) {
stampProp = new Properties()
stampProp.load(new FileInputStream(tmpTimestampFile))
def timestampFromFile=stampProp.getProperty('propfileTimestamp')
def unzippedExtFilesDir = file("$buildDir/tmp/unzipped/static/js/app")
def appBuildDir = file("$buildDir/apptmp/war/app/app_$timestampFromFile/sub")
from unzippedExtFilesDir
into appBuildDir
}
}
First time through my build (after a clean) it says 'UP TO DATE" (debug output says it's skipping the task as it has no source files.)
I have tried adding closures to defer evaluation, (from { unzippedExtFilesDir }) and tried putting the body of the task in a doFirst, but neither had any effect. I've also tried using the dependsOn task name (unzipExtFile) directly in the 'from' statement but that also did not work.
Second time the build is run this task does get run, so it appears to be a timing thing between the configuration and execution, but I'm running out of ideas. Any suggestions would be welcome.
The code is reading the timestamp at configuration time, before any task has run. (You can see this by adding a println statement.) Because no timestamp file exists at this time (in particular on the first run), no source location (from ...) gets configured, and the task is considered up-to-date because there is nothing to copy. Putting the whole timestamp logic into the into block (into { ...; appBuildDir }) should solve the problem.

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