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

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.

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?

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.

Passing property to custom gradle task from command prompt

I wrote a custom gradle task class (say PrintNameTask) that accepts some input parameter (say name).
Then if I define a printName task of type PrintNameTask like below:
task printName(type: PrintNameTask) {
name = project.name
}
and invoke it from command prompt like below I can see the passed name printed out
$gradle printName -Pname=myName
myName
However if I invoke any other task like clean or build the build fails because there is no property called name passed. This is fair enough as my printName is a configure closure and is evaluated all the times.
To address this I tried to change the configure closure into a task action closure like below:
task printName(type: PrintNameTask) << {
// What should I put on here?
name = ???
// or
name ???
}
But it was no way to make it work. I tried project.name, getProperty("name") and a few more other combinations but nothing worked. All I get back is:
* What went wrong:
A problem was found with the configuration of task ':printName'.
No value has been specified for property 'name'.
This kind of requirement looks to me quite basic and it is a bit frustrating that tons of books and documentation are published but they only shows trivial examples. Maybe is just me but at the point of asking this question my initial gradle enthusiasm is more than half gone. Anyway thank you in advance for your inputs.
Configure the task in the following way:
task printName(type: PrintNameTask) {
name = project.hasProperty('name') ? project.name : '' // or null
}
Since this closure is evaluated at configuration phase it's executed every time the script is processed. You just need to check if the property is present.

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!

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