Aggregate tasks in Gradle - groovy

I'm working on a gradle task that depends on other gradle tasks, i.e an aggregate task. The aggregate task required the other tasks to run in a certain linear order too.
Note: The other tasks must be able to run in isolation. The dependencies between the tasks ONLY makes sense when running the aggregate task.
My first attempt was to use someTask.execute(), but then I realized that this was not the Gradle way, i.e it is not a supported feature and that the internal API might change.
task aggregate() {
doFirst {
// Need to set properties required by other tasks
project.ext.somePropRequiredByTask1 = propertyX + "-" propertyY
}
doLast {
task1.execute()
task2.execute()
task3.execute()
}
}
After reading up on creating aggregate tasks, this is my current solution:
task aggregate() {
// Only configure task if the task is exlicitly run
if (gradle.startParamter.taskNames.contains("aggregate") {
project.ext.somePropRequiredByTask1 = propertyX + "-" propertyY
chainTaskDependencies([task1, task2, task3])
}
}
// Create a strict dependsOn dependency order for the tasks in the task list
def chainTaskDependencies(List<Task> tasks) {
def taskNames = tasks.collect { it.name}
println "Run tasks in this order: " taskNames.join " -> "
def reversed = tasks.reverse()
for (int i = 0; i< reversed.size() -1 ; i ++) {
reversed[i].dependsOn(reversed[i+1])
}
}
Is this the correct way to create an aggregate task dependency where the order between tasks is linear? How can it be improved?

How about just extracting shutdownVM, waitForVMShutdown and rollback to functions/closures that your tasks call ? Then you are free to compose them in any which way you choose.
I've found this to be the most flexible option in similar cases.

B.mustRunAfter(A)
means that if both A and B run then B must run after A. B can be run by itself no problems.
So:
task A
task B
task C
task aggregate(dependsOn(['A', 'B', 'C'])
C.mustRunAfter(B)
B.mustRunAfter(A)

As Gradle team member and Spock inventor Peter Neiderweiser puts it here:
You can't, because a task (instance) will be executed at most once per build. Instead, the way to go is to declare multiple task instances. You could do this by putting the task declaration in a loop, putting it in a method and calling it multiple times, or by writing a task class and instantiating it multiple times. Then you'll add one other task that depends on all bulk tasks. Assuming the execution order between bulk tasks is irrelevant, that's it.
He also suggests using task rules for this purpose.
The OP also posts the solution he or she devised, so you can look at that as well.

Related

Request clarification of Gradle task rule syntax from documentation

Pursuant to this spot in the Gradle documentation:
https://docs.gradle.org/current/userguide/more_about_tasks.html#N10F07
tasks.addRule("Pattern: ping<ID>") { String taskName ->
if (taskName.startsWith("ping")) {
task(taskName) << {
println "Pinging: " + (taskName - 'ping')
}
}
}
It says, "Sometimes you want to have a task whose behavior depends on a large or infinite number value range of parameters. A very nice and expressive way to provide such tasks are task rules.".
It's not at all clear to me from this block of code how those parameters are defined nor how they get into the task scope so that they can be used.
It's also not clear if they have to be all the same type. I really need an example that shows these.
I'm not a Groovy guy in the first place. Gradle is my reason for learning Groovy.

Grade: Can't access configuration defined in one project from another project

I'm pretty new to both gradle and groovy.
The Problem
I've got a very simple multi-project structure just like below:
Root project 'gradle_test'
+--- Project ':sub1'
\--- Project ':sub2'
This is what the 'build.grade' file looks like for sub1 project:
// build.gradle of sub1 project
task testConfiguration {
println project(':sub2').configurations.sub2FooConfiguration
}
And finally, this is the 'build.grade' file of sub2 project:
// build.gradle of sub2 project
configurations {
sub2FooConfiguration
}
Very minimum. Now, if I run gradle :sub1:testConfiguration, I got the following error:
A problem occurred evaluating project ':sub1'.
> Could not find property 'sub2FooConfiguration' on configuration container.
However, everything just works if the testConfiguration task in sub1 project is modified like this:
// notice the "<<" (I believe this is calling the 'doLast' method on the task instance)
task testConfiguration << {
println project(':sub2').configurations.sub2FooConfiguration
}
The Question
What I assume the difference between the two versions of the 'testConfiguration' task is that in the first instance, a configuration closure is passed to the task whereas in the modified version a 'normal' closure is passed to the 'doLast' method.
So, first of all, is my assumption correct?
Secondly, why I don't have access to 'sub2' project in the first instance?
And finally, is it possible to access 'sub2' project in the first instance (i.e. in the configuration closure)?
[Update] A Further Question
Given the accepted answer provided by "Invisible Arrow", I'd like to ask a further question regarding the best practice of referencing a configuration of another project (i.e. a task in sub1 needs to use an archive produced by sub2 project).
Should I declare evaluation dependency between the two projects?
Or Should I only reference sub2's configuration at execution time (e.g. in doLast())?
Or, should I create a dependency configuration between the two projects?
Yes, there is a difference between the two.
There are essentially 3 phases to a build which are Initialization, Configuration and Execution. This is described in detail under the Build Lifecycle chapter in the Gradle documentation.
In your case, the first instance falls under the Configuration phase, which is always evaluated irrespective of whether the task is executed or not. That means all statements within the closure are executed when you start a build.
task testConfiguration {
// This always runs during a build,
// irrespective of whether the task is executed or not
println project(':sub2').configurations.sub2FooConfiguration
}
The second instance falls under the Execution phase. Note that << is a shorthand for doLast, and this closure is called when the task is executed.
task testConfiguration << {
// Called during actual execution of the task,
// and called only if the task was scheduled to be executed.
// Note that Configuration phase for both projects are complete at this point,
// which is why :sub1 is able to access :sub2's configurations.sub2FooConfiguration
println project(':sub2').configurations.sub2FooConfiguration
}
Now coming to why the first instance gave the error. This was because the Configuration phase of sub2 project was not evaluated yet. Hence the sub2FooConfiguration wasn't yet created.
Why? Because there is no explicit evaluation dependency between sub1 and sub2. In your case, sub1 needs sub2 as an evaluation dependency, hence we can add that dependency in sub1's build.gradle before the task declaration, as follows:
evaluationDependsOn(':sub2')
task testConfiguration {
println project(':sub2').configurations.sub2FooConfiguration
}
This will ensure that sub2 is always evaluated before sub1 (evaluation meaning the Configuration phase for the projects). sub1 will now be able to access configurations.sub2FooConfiguration in the task declaration closure. This is explained in detail in the Multi-project Builds chapter.
In the second instance, configurations.sub2FooConfiguration was accessible, as the call was in the execution block of the task (which is after the Configuration phase for both projects).
PS: Note that if you reversed the names of the projects, then the first instance might actually work, as Gradle configures projects alphabetically if there are no explicit dependencies. But, of course, you should never rely on this and ensure that the dependencies between projects are declared explicitly.

What's the operator << (double less than) in gradle?

The following piece of code defines 4 gradle tasks:
4.times { counter ->
task "task$counter" << {
println "I'm task number $counter"
}
}
But what is the << operator? What does it do in groovy?
The << is a left-shift operator. In this scenario, task "task$counter" is a Task object declaration, and << is overloaded as an alias to the doLast method, which appends the closure to the list of actions to perform when executing the task.
If you don't specify the <<, the closure is treated as a configuration closure and will be executed by default during the configuration phase of your project's build lifecycle, regardless of whatever task argument is given on the command line.
Example:
If you take the configuration specified in the question:
4.times { counter ->
task "task$counter" << {
println "I'm task number $counter"
}
}
And run gradle task3, the output will be:
:task3
I'm task number 3
Because each closure was defined to be an execution action specific to the task. Since task3 was named as the task to execute, that was the only action closure executed.
But if you remove the << and make the configuration as follows:
4.times { counter ->
task "task$counter" {
println "I'm task number $counter"
}
}
And run gradle task3, the output will then be:
I'm task number 0
I'm task number 1
I'm task number 2
I'm task number 3
:task3 UP-TO-DATE
This is because all closures were defined to configure the tasks themselves, not be executed as actions when running the tasks. So in this case, Gradle executed all of the closures while configuring the project, then when it came time to execute task3, there were no actions to perform, so it reported the task as UP-TO-DATE.
Basically this is a leftShift operator - You can find more details here.
In gradle << operator is used to add action to a particular task. A task consists of multiple actions that are run (in order they were added) during the execution of the task. << just adds an action to tasks collection of actions. More about tasks and actions can be found here.

Publish artifacts using base plugin

I want to publish few artifacts using base plugin. This is how my build looks like:
apply plugin: 'base'
group = 'eu.com'
version = '0.9'
def winCdZip = file('dist/winCd.zip')
configurations {
wincd
}
repositories {
ivy {
url 'http://ivy.repo'
}
}
artifacts {
wincd winCdZip
}
buildscript {
repositories {
ivy {
url 'http://ivy.repo'
}
}
dependencies {
classpath group: 'eu.com', name:'MyCustomTask', version:'0.9-SNAPSHOT', configuration: 'runtime'
}
}
buildWincd {
// call MyCustomTask; is it possible to call it in this way?
MyCustomTask {
// pass few parameters required by this task
}
// I know that it's impossible to call zip in this way but I don't want to create another task
zip {
// build zip and save it in 'winCdZip'
}
}
uploadWincd {
repositories { add project.repositories.ivy }
}
And those are my problems to solve:
Is it possible to create nested tasks?
Is it possible to call zip without create new task but with closures?
Is it possible to call custom task using closures (the same example as at 2nd point)?
I can create zip/custom task in this way
task myZip(type: Zip) {
// do the job
}
is it possible to call it in this way?
zip {
// do the job
}
If it is not possible to call tasks using closures, how can I do it? Creating new tasks is the only way? Maybe I can create nested tasks?
The answer to your questions is 'no'. Gradle is a declarative build system. Instead of having one task call another, you declare task dependencies, which Gradle will obey during execution.
For some task types (e.g. Copy), there is an equivalent method (e.g. project.copy), but not for Zip. In most cases, it's better to use a task even if a method exists.
The first several chapters of the Gradle User Guide explain core Gradle concepts such as task dependencies in detail.

Gradle task definition inheritance

Is it possible to inherit one task definition from another? What I want to do is create some test profiles, so I'd have default test -
test {
include 'com/something/something/**'
exclude 'com/something/else/**'
maxParallelForks 5
testLogging{
exceptionFormat "full"
showStackTraces = false
}
jvmArgs '-Xms128m', '-Xmx512m', '-XX:MaxPermSize=128m'
}
and some another test with overriden "include" or "maxParallelForks" part etc.
Is it possible without creating new Task class?
You could configure all those tasks in one go, provided they're of the same type using the following construct:
tasks.withType(Test) {
include 'com/something/something/**
...
}
This configures all the tasks of type "Test" in one go. After that you can override the configurations.
or if you don't want to setup all the tasks, or some of them have a different type, you can enumerate them as in the following snippet.
["test","anotherTestTask"].each { name ->
task "$name" {
include ...
}
}
Remember, you have the full scripting power of Groovy, so there are a lot of options here...

Resources