Gradle: task's configuration depends on another task's execution - gradle

My Gradle build has two task:
findRevision(type: SvnInfo)
buildWAR(type: MavenExec, dependsOn: findRevision)
Both tasks are configuration based, but the buildWAR task depends on a project property that is only defined in the execution phase of the findRevision task.
This breaks the process, as Gradle cannot find said property at the time it tries to configure the buildWAR task.
Is there any way to delay binding or configuration until another task has executed?
In this specific case I can make use of the mavenexec method instead of the MavenExec task type, but what should be done in similar scenarios where no alternative method exists?

Depending on what configuration option exactly you want to change, you might change it in the execution phase of the task with buildWAR.doFirst { }. But generally this is a really bad idea. If you e. g. change something that influences the result of the UP-TO-DATE checks like input files for example, the task might execute though it would not be necessary or even worse do not execute thoug it would be necessary. You can of course make the task always execute to overcome this with outputs.upToDateWhen { false }, but there might be other problems and also this way you disable one of Gradles biggest strenghts.
It is a much better idea to redesign your build so that this is not necessary. For example determining the revision at configuration time already. Depending on whether the task needs much time this might be a viable solution or not. Also depending on what you want to do with the revision, you might consider the suggestion of #LanceJava and make your findRevision task generate a file with the revision in it that is then packaged into the WAR and used at runtime.

Related

Troubleshooting Gradle Performance Issue

Recently our build times have been fluxuating wildly. Our baseline was between 1-2mins, but now it is sometimes taking up to 20 mins. I have been trying to track down the cause, but am having some trouble. It feels like the changes that trigger fluxuation in the build time are completely arbitrary, or at least I can not find the underlying connection between them.
Here is what the current build time looks like:
Now this obviously looks like the issue lies in Task Execution time, so lets look at that in more detail:
This indicates that the test task for the umlgen-transformer project is the culprit. The only difference between this project and the one that built in ~1min, is one tiny xml file. That xml file is read in by each of the unit tests as input. Here is what a typical test looks like:
#Test
def void testComponentNamesUpdated() {
loadModel("/testData/ProducerConsumer.uml")
val trafo = new ComponentTransformation
trafo.execute(model)
val componentNames = model.allOwnedElements.filter(Component).map[name]
assertThat(componentNames).contains(#{"ProducerComponent", "ConsumerComponent"})
}
(the .uml file is just a UML model stored as xml). There are several reasons why I don't think this is the underlying cause of our increases in build times: 1) the xml file is incredily small, ~200kb 2) the behavior in the unit tests is extremly simple and executes quite fast 3) when the unit tests are run through eclipse they execute in seconds 4) the build times of unrelated aspects, such as the time to compile kotlin code, have also seen a significant increase 5) this is not the only project we are having this issue with.
What I have tried:
Setting org.gradle.parallel = true
Increasing the memory of gradle deamon to 3gb
Updating to gradle 5
Clearing all gradle caches
Building without the gradle daemon
Simplifying the build.gradle scripts. I have removed several plugins and tasks to rule them out as possible causes
I have only been using Gradle for a couple months now, so I am hoping to get feedback on more conclusive ways to find out what gradle is doing (or to find some way to cross it off the list of possible causes). Please let me know if any additional information would be helpful.

Run task only if another task wasn't UP-TO-DATE

I'm new to gradle, but I can't find this issue addressed anywhere in the way I descibe here:
1) I have a task versionUpdate that increments a build number counter in a number of files. (The task is arbitrary; My question is about defining complex graph dependencies.)
2) I only want versionUpdate to execute if the compile task is not UP-TO-DATE. This is a multi-project build, and it needs to happen if any subproject builds, and only once.
3) versionUpdate should happen before compile (as it must reflect the current build number), but if and only if compile was added to the graph. That is, not all tasks should be invoking versionUpdate, and even compile is conditional.
Currently, I just have the following:
[compileJava, compileTestJava]*.dependsOn versionUpdate
TL;DR How can I ask the compile task if it's UP-TO-DATE and modify the task graph based upon this information?
You can achieve what you want by adding a doFirst block to the compileJava task:
compileJava.doFirst {
// your code to update versions goes here
}
This will only be executed if compileJava actually has work to do

"Up To Date" Gradle task status when it has no output

How can you correctly mark a Gradle task as being "up to date" when the task doesn't produce any output? The task should remain "up to date" provided the last run was successful and the inputs haven't changed since then. The Gradle guide states just before section 15.9.2, the following:
"A task with no defined outputs will never be considered up-to-date."
How is it possible to mark tasks as up to date in this case? It appears that Gradle needs to know the time of the last successful run and then compare that to the last modified time of the inputs. As a workaround the script could create / touch an empty file to mark the task as complete? Are there any other suggested workarounds?
To just think through the different scenarios...
Tasks without any inputs or outputs. These run all the time. This might be just wrapping an existing "do something" executable.
Tasks with inputs and outputs. These run when either the inputs or outputs change. This might be a compiler.
Tasks with just outputs. These run only when the outputs have changed/don't exist. This might be something that downloads something. (I think these are pretty rare in reality, I'd count the URL to download as an input.)
Tasks with inputs and no outputs. I haven't run into these in practice.
Like you've said, you could cheat the up-to-date checks with an output file that is just empty. The built-in Gradle Test task is most similar to what you're describing and it has a "report" as its output. I think you would probably have something similar too. It could be as simple as capturing the stdout/stderr of the task and putting that into a file. That's not too useful for when everything passes, but it would be useful for when things fail.
Of course, any of these could be supplemented with a custom upToDateWhen bit of code. e.g., you have a task that starts a webserver and it's "up-to-date" when the webserver is already running. I don't think that's a good fit with what you're describing here.
To start out with, I'd try:
outputs.files file("${buildDir}/reports/${name}.out")
I think that'd work with or without actually putting something in the file.
You can override the tasks upToDateWhen method.
task createDist(type: Zip) {
outputs.upToDateWhen {
return true
}
}
So with true it is always up-of-date and false always out-of-date. You can define any kind of custom logic to determine if it is up to date.
Tasks with inputs and no outputs
Unless you explicitly declare the output, Gradle will decide the output is out-of-date to stay build safe.
You should always patently declare both inputs and outputs.
Use outputs.upToDateWhen { true } to simultaneously declare outputs and indicate that your outputs are always up-to-date.
For example (in Kotlin):
task ("dataFileHandler") {
inputs.file ("data-file.txt")
outputs.upToDateWhen { true }
doLast {
// do something with "data-file.txt"
}
}
This task will be executed once (on the first build) and will remain “UP-TO-DATA” until “data-file.txt” is changed.

Setting System Properties for Gradle Tests

I have an application in which each test should run in a VM whose configuration has been conditioned by a test-specific System property (which can just be based on the test class name).
Something like this sort of works:
test {
forkEvery 1
maxParallelForks Runtime.runtime.availableProcessors()
beforeSuite { TestDescriptor descriptor ->
systemProperty( 'test.class.name', descriptor.getClassName() )
}
}
But it doesn't quite work. The names the forked JVMs see do change, but they aren't the ones I expect, and I suspect they aren't even deterministic. It seems as though there is one shared JavaForkOptions item, and that calls to beforeSuite aren't in a deterministic and exclusive way linked to the forking of a process for that suite, so the name a process gets might not match up with the one set in "its" beforeSuite call.
Is there a better way to do this, or some way to get more precise control over the forking process so that System properties could be set on a fork-specific data structure?
Thanks for any help!

Better to use task dependencies or task.doLast in Gradle?

After building my final output file with Gradle I want to do 2 things. Update a local version.properties file and copy the final output final to some specific directory for archiving. Let's assume I already have 2 methods implemented that do exactly what I just described, updateVersionProperties() and archiveOutputFile().
I'm know wondering what's the best way to do this...
Alternative A:
assembleRelease.doLast {
updateVersionProperties()
archiveOutputFile()
}
Alternative B:
task myBuildTask(dependsOn: assembleRelease) << {
updateVersionProperties()
archiveOutputFile()
}
And here I would call myBuildTask instead of assembleRelease as in alternative A.
Which one is the recommended way of doing this and why? Is there any advantage of one over the other? Would like some clarification please... :)
Whenever you can, model new activities as separate tasks. (In your case, you might add two more tasks.) This has many advantages:
Better feedback as to which activity is currently executing or failed
Ability to declare task inputs and outputs (reaping all benefits that come from this)
Ability to reuse existing task types
More possibilities for Gradle to execute tasks in parallel
Etc.
Sometimes it isn't easily possible to model an activity as a separate task. (One example is when it's necessary to post-process the outputs of an existing task in-place. Doing this in a separate task would result in the original task never being up-to-date on subsequent runs.) Only then the activity should be attached to an existing task with doLast.

Resources