How to determine if a Gradle task was instantiated (Configuration Avoidance API) - gradle

I am trying to improve the performance of our Gradle builds and discovered the Gradle Task Configuration Avoidance API (https://docs.gradle.org/current/userguide/task_configuration_avoidance.html). It allows to postpone the creation and configuration of a task, unless it is really needed. This might save a lot of startup time and as we call Gradle multiple times during a build, this could amount to considerable time savings.
We developed some plugins for internal use and I put an effort into changing how I define the tasks to avoid creation when not needed. I want to test if my changes are successful and the task instantiation is delayed.
Simple example of how to create a task without instantiating it:
class MyTask extends DefaultTask {
}
TaskProvider customTask = tasks.register("customAction", MyTask) {
println "task configured!" // configuration time output
doLast {
println "action 1!" // execution time output
}
}
// configuration like this avoids task instantiation
tasks.named("customAction") {
doLast {
println "action 2!"
}
}
tasks.withType(MyTask).configureEach {
doLast {
println "action 3!"
}
}
Executing gradle help does not print the "task configured!" message, while gradle customAction does.
To make sure I do not accidentally trigger task instantiation, I would like to write tests for our plugins. But I could not find a way to determine if a task is instantiated or not.
I know about Build Scans (https://guides.gradle.org/creating-build-scans/), but our corporate guidelines are strict and clearance is pending, so I can not use it for now. Also, I do not see a way to use it in tests.
Is there a way to
get a list of created/instantiated tasks from the Gradle project?
or is there any property on Task or TaskProvider showing whether the task has been created/instantiated?
or can buildscans be used offline somehow?
It would be cool, if the solution could be used in the plugin's test code, but manual evaluation would also be valuable.

configureEach called only when task is created. you can write something like this to get list of all configured tasks
def tasks = []
project.allprojects { Project sp ->
sp.tasks.configureEach { Task t ->
tasks,add(t.path)
}
}
project.gradle.buildFinished {
println tasks
}

This is not exactly what OP asked for, but this will give you some stats on how many of each type of task were configured:
gradlew :help -Dorg.gradle.internal.tasks.stats

Related

How to run filtering tests in gradle?

I have to run tests in a specific order using build.gradle file.
I have the build.gradle file looks like the following:
test {
include 'com.my-project.MyTestClass'
include 'com.my-project.MyTestClass1'
}
but when I running test task I have the following message:
Tests event were not received
How can I fix this problem?
That message just means that no tests were actually run. There could be a number of reasons for that, but the most likely given your example is that the include method takes an Ant style file pattern, but you have given it (fully qualified) class names. Also, 'my-project' is not a valid package name, but I assume this is just a error in your example here.
But more importantly, if your intent is to run tests in a specific order, you will not achieve that with a single test task. The specified includes just tell Gradle what tests are part of the suite, but doesn't affect the order.
I don't know what test framework you are using, but I also don't think it is possible with JUnit 4 and 5. The only way I can think of is to create multiple Test tasks in Gradle, where each task represent a single unit test (or group of tests that can be run in any order), and where you order each task through dependsOn. So something like this:
task myTest1(type: Test) {
include 'example/MyTestClass1.class'
}
task myTest2(type: Test) {
dependsOn myTest1
include 'example/MyTestClass2.class'
}
test {
exclude 'example/**'
dependsOn myTest2
}

gradle create custom tasks

I'm a little bit confused about the correct way to create custom tasks on Gradle. On the tutorial for the Creation of custom tasks, they use tasks.register like this:
def check = tasks.register("check")
def verificationTask = tasks.register("verificationTask") {
// Configure verificationTask
}
check.configure {
dependsOn verificationTask
}
Instead here (still official Gradle documentation), they create new tasks that way:
task('hello') {
doLast {
println "hello"
}
}
task('copy', type: Copy) {
from(file('srcDir'))
into(buildDir)
}
and
tasks.create('hello') {
doLast {
println "hello"
}
}
tasks.create('copy', Copy) {
from(file('srcDir'))
into(buildDir)
}
Finally, according to the document https://docs.gradle.org/current/userguide/task_configuration_avoidance.html, they suggest to move from the second/third case to the first one. Does it mean that the second/third cases are obsolete? If yes, why is Gradle still making massive usage of the old API inside its documentation?
Which variant should a user use?
The Gradle API has many ways to define tasks. There is no "right" or "wrong" way for application developers so long as you are consistent, but it does matter for Gradle plugin authors.
The Task Configuration Avoidance doc you linked states (emphasis mine):
As of Gradle 5.1, we recommend that the configuration avoidance APIs be used whenever tasks are created by custom plugins.
So if you are a plugin author, use task configuration avoidance wherever possible
For everyone else (application developers), it doesn't particularly matter, to an extent, so long as your as consistent across your entire application.

Gradle custom task action order

I'm looking at a simple example of a custom task in a Gradle build file from Mastering Gradle by Mainak Mitra (page 70). The build script is:
println "Working on custom task in build script"
class SampleTask extends DefaultTask {
String systemName = "DefaultMachineName"
String systemGroup = "DefaultSystemGroup"
#TaskAction
def action1() {
println "System Name is "+systemName+" and group is "+systemGroup
}
#TaskAction
def action2() {
println 'Adding multiple actions for refactoring'
}
}
task hello(type: SampleTask)
hello {
systemName='MyDevelopmentMachine'
systemGroup='Development'
}
hello.doFirst {println "Executing first statement "}
hello.doLast {println "Executing last statement "}
If I run the build script with gradle -q :hello, the output is:
Executing first statement
System Name is MyDevelopmentMachine and group is Development
Adding multiple actions for refactoring
Executing last statement
As expected with the doFirst method excecuting first, the two custom actions executing in the order in which they were defined, then the doLast action executing. If I comment out the lines adding the doFirst and doLast actions, the output is:
Adding multiple actions for refactoring
System Name is MyDevelopmentMachine and group is Development
The custom actions are now executing in the reverse order in which they are defined. I'm not sure why.
I think it's simply a case that the ordering is not deterministic, and you get different results depending on how you further configure the task.
Why do you want 2 separate #TaskAction methods as opposed to a single one that calls methods in a deterministic sequence, though ? I can't think of a particular advantage of doing it that way (I realize it's from a sample given in a book).
Most other samples I find only have a single method
#TaskAction
void execute() {...}
which I think makes more sense and be more predictable.
Patrice M. is correct, the way those methods will be executed is undeterministic.
In detail
#TaskAction annotated methods are being processed by AnnotationProcessingTaskFactory.
But first Task action methods are fetched with DefaultTaskClassInfoStore and results stored in TaskClassInfo.
You can see that Class.getDeclaredMethods() is being used to fetch all methods to check if they contain #TaskAction annotation
And here the definition of public Method[] getDeclaredMethods() throws SecurityException
Description contains following note:
The elements in the returned array are not sorted and are not in any
particular order.
Link to Gradle discussion forum with the topic about #TaskAction
It doesn't guarantee the order.
For your information, just added one more link which was an issue I raised few years ago, it should be giving warnings or replaced with some other better solutions by Gradle.
https://github.com/gradle/gradle/issues/8118

Referencing the outputs of a task in another project in Gradle

Consider the following setup
rootProject
|--projectA
|--projectB
There is a task taskB in projectB and I would like the reference the outputs of that task in a copy task taskA in projectA. For example, taskA might look something like:
task taskA(type: Copy) {
dependsOn ':projectB:taskB'
from ':projectB:taskB.outputs'
into 'someFolder'
}
Of course the above example doesn't actually work. While it's okay to reference the task as :projectB:taskB as a dependency, :projectB:taskB.outputs doesn't seem to mean anything to Gradle. I've tried reading through the Gradle docs but didn't find anything that referenced what I'm trying to do.
The accepted answer has been to only and recommended way to solve this problem. However building up this kind of project dependencies and reaching from one project into another is discouraged by the Gradle team now. Instead of this, projects should only interact with each others using publication variants. So the idiomatic (but sadly at the moment more verbose) way would be:
On the producing side (projectB) define a configuration that is not resolvable but consumable by other projects and creates a new variant (called taskB-variant)
configurations.create("taskElements") {
isCanBeResolved = false
isCanBeConsumed = true
attributes {
attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage::class, "taskB-variant"))
}
outgoing.artifact(taskB.outputs)
}
On the consuming side (projectA) define a configuration that is resolvable but not consumable for the same variant and define a dependency to projectB
val taskOutputs by configurations.creating {
isCanBeResolved = true
isCanBeConsumed = false
attributes {
attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage::class, "taskB-variant"))
}
}
dependencies {
taskOutputs(project(":projectB"))
}
tasks.register<Copy>("taskA") {
from taskOutputs
into 'someFolder'
}
This way you decouple how the outputs are produced and the publication variant (called "taskB-variant") becomes the interface between projectA and projectB. So whenever you change the way the output is created you only need to refactor projectB but not projectA as long as you make sure the outputs end up in the taskElements configuration.
At the moment this is still pretty verbose but hopefully Gradle will get more powerful APIs to describe this kind of project relationships in the future.
projectA build.gradle should be:
evaluationDependsOn(':projectB')
task taskA(type:Copy, dependsOn:':projectB:taskB'){
from tasks.getByPath(':projectB:taskB').outputs
into 'someFolder'
}

What's the best choice between tasks or methods to organize your Gradle build logic?

I'm currently migrating some old huge Maven 1 script to Gradle.
As a consequence, I need to adapt the old Maven 1 / Ant & its goals logic to Gradle.
After having read the Gradle User Guide, and some articles on Gradle tasks and methods, I am quite confused about the way to write my script.
In the official Gradle User Guide, §6.1, it is explained that a Gradle task:
represents some atomic piece of work which a build performs
In §6.11, it is also explained that we can use methods to organize our build logic.
So, my question is: what's the correct way to use each of them?
I am creating a build script so, in my opinion:
tasks should only be what the user is allowed to see, and so, to call through the command line.
By example gradle doSomeInternalTechnicalWork is not correct for me, as the user should even not know that doSomeInternalTechnicalWork exists.
Always in my opinion, it should NOT be a task.
method should be used to organize the build logic, and should NOT be visible by the user
With the former logic, I encounter problems when my methods need to call Gradle tasks (like the JAR creation of the Java plugin).
I know that I should not call task from task (and so the same for task from method), but, have a look to this example:
task independentTask << {
// initialization stuff
println "doing a lot of initialization"
// using methods to organize build logic, good or not?
doComplexThingsThatTheUserShouldNeverDoHimself()
}
task dependentTask(dependsOn: 'independentTask') << {
println "now that 'independentTask' is done, I can continue to do complex things..."
}
void doComplexThingsThatTheUserShouldNeverDoHimself() {
println "doing really complex things"
// I really need to create my JAR here and not somewhere else
// And I know it's not a good thing to directly call the Action.execute
jar.execute()
println "doing other really complex things"
}
In this case, what would be a correct build logic?
Should doComplexThingsThatTheUserShouldNeverDoHimself be converted in 1 or more tasks, so as to be able to dependsOn the JAR task?
But that would mean to have really a lot of tasks, callable by the user, when, indeed, that should not be the case.
After having search quite a lot, I concluded that, when you need to call a task from another task, you have little choice but to rely on tasks relationships (dependsOn, mustRunAfter, finalizedBy).
Which means that methods can't be used to organize the build logic in the same way that they are used to structure a program in Java, Groovy & Co.
As a consequence, you can't prevent the user from seeing (and using) some internal tasks, that should normally only be used as dependency by some other ones.
A "Gradle correct" version of the former build script would hence be:
task dependentTask(dependsOn: 'doComplexThingsThatTheUserShouldNeverDoHimselfPart2') << {
println "now that 'independentTask' is done, I can continue to do complex things..."
}
task doComplexThingsThatTheUserShouldNeverDoHimselfPart2(dependsOn: ['doComplexThingsThatTheUserShouldNeverDoHimselfPart1', 'jar']) << {
println "doing other really complex things"
}
task doComplexThingsThatTheUserShouldNeverDoHimselfPart1(dependsOn: 'independentTask') << {
println "doing really complex things"
}
task independentTask << {
// initialization stuff
println "doing a lot of initialization"
}
Or, with tasks relationships declared separately:
task dependentTask << {
println "now that 'independentTask' is done, I can continue to do complex things..."
}
task independentTask << {
// initialization stuff
println "doing a lot of initialization"
}
task doComplexThingsThatTheUserShouldNeverDoHimselfPart1 << {
println "doing really complex things"
}
task doComplexThingsThatTheUserShouldNeverDoHimselfPart2 << {
println "doing other really complex things"
}
// we declare all tasks relationships separately
dependenTask.dependsOn doComplexThingsThatTheUserShouldNeverDoHimselfPart2
doComplexThingsThatTheUserShouldNeverDoHimselfPart2 dependsOn doComplexThingsThatTheUserShouldNeverDoHimselfPart1, jar
doComplexThingsThatTheUserShouldNeverDoHimselfPart1 dependsOn independentTask
Personally, I prefer the last one, the relationship block being more readable.

Resources