Run gradle task after doLast of a other task - java

I have a variable in my build.gradle (xy) file with an integer value and three simple tasks. The tree tasks are also "bundled" in a fourth task, to call the three tasks one after one. If I call the task callAll, the task callOne should add 5 to the int value, task callTwo should add also 5 the int value, but in a doLast block and finally task callThree prints out the value. It should be 10 but it prints 5, I guess because of the doLast block. But I dont understand why, I declared to call task callThree it mustRunAfter callTwo in the task callAll.
This example is of course a simplified version of a more complex build.gradle I have. How can i get the right output, without changing the three simple tasks?
project.ext{
xy= 0
}
task callOne(){
xy = xy + 5
}
task callTwo(){
doLast{
xy = xy + 5
}
}
task callThree(){
println "xy : $xy"
}
task callAll(){
dependsOn 'callOne'
dependsOn 'callTwo'
dependsOn 'callThree'
tasks.findByName('callTwo').mustRunAfter 'callOne'
tasks.findByName('callThree').mustRunAfter 'callTwo'
}

A Gradle build runs three distinct phases: Initialization, Configuration and Execution (Documentation).
In the configuration phase, the project and its tasks are configured. During this phase:
callOne sets xy = xy + 5
callTwo does nothing
callThree prints println "xy : $xy"
Tasks are executed in the execution phase of the Gradle build, this means:
callOne does nothing
callTwo sets xy = xy + 5
callThree does nothing
5 is printed rather than 10 since you're mixing configuration and execution phases.
callAll is more or less only a custom lifecycle task that configures the task graph. In itself, callAll performs no calls to the other tasks and consequently does nothing in the execution phase.
You may get your desired behavior by enclosing all task actions in a doFirst {} or doLast {} block.
Please have a look at Configuration time and execution time for more information.

Related

How to have many configurations of a task in gradle builds?

I am currently using a plugin to run benchmarks on Java code, and I used jmh-gradle-plugin to do that.
The plugin allows to conveniently describe JMH configurations with a jmh code block:
jmh {
include = ["List"]
benchmarkParameters=["seed": ["1", "2", "3", "4", "5"],
"applicationSize":["100", "1000"],
"baseStructureSize":["100", "1000"]]
fork=2
timeOnIteration="250ms"
iterations=2
warmup="250ms"
warmupIterations=2
fork=2
resultFormat="CSV"
benchmarkMode=["ss"]
timeUnit="ms"
failOnError = true
}
This is useful, but I would like to have different types of the same task, for instance, one where the output is CSV, one where the output is JSON. I know this can be configured with resultFormat=<format>, but I could not find a way to "duplicate" a task and have a different configuration for each variant.
The Gradle documentation has a page about configuring tasks, but they configure a Copy task. I thought I could follow a similar approach and write:
task myJMH(type: me.champeau.gradle.JMHTask) {
resultFormat="JSON"
}
But approach does not work, as I mentioned in this issue. I think it might be that the JMH task is just different. Registering a class of that name works, but it's not possible to configure it. I get the following error:
Could not set unknown property 'include' for task ':myJMH' of type me.champeau.gradle.JMHTask.
Similarly, I would like to have various configurations of the shadowJar task, to be able to generate several different variants of the task, but I had the same problem.
The jmh in your first example is not a task, but an extension. The plugin registers both an extension and a task with the same name. Actually, this is a prevalent pattern for Gradle plugins.
Usually, even if the tasks created by a plugin may be configured using an extension, it is still possible to configure them directly, as they still provide configuration properties. This is the case for the task type ShadowJar, so you can simply create tasks of that type manually:
// Shadowing Test Sources and Dependencies
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
task testJar(type: ShadowJar) {
classifier = 'tests'
from sourceSets.test.output
configurations = [project.configurations.testRuntime]
}
Sadly, the task type JMHTask is implemented in a way that it simply retrieves the configuration from the extension, so any instance will use the same configuration.
However, you may try the following workaround:
Create tasks that configure the extension and then wire them to be executed together with the jmh task:
jmh {
include = ["List"]
benchmarkParameters=["seed": ["1", "2", "3", "4", "5"],
"applicationSize":["100", "1000"],
"baseStructureSize":["100", "1000"]]
fork=2
timeOnIteration="250ms"
iterations=2
warmup="250ms"
warmupIterations=2
fork=2
benchmarkMode=["ss"]
timeUnit="ms"
failOnError = true
}
task jmhCsv {
doFirst {
jmh.resultFormat="CSV"
}
finalizedBy 'jmh'
}
task jmhJson {
doFirst {
jmh.resultFormat="JSON"
}
finalizedBy 'jmh'
}
Please note that a task may only be executed once in a build, so this workaround won't work if you want to run different configurations in the same run.

Why there are 2 ways to create a task in gradle?

I'm currently writing a gradle script to automate some builds. However it seems that there are 2 ways to create tasks. What should I take and why are there different types in first place?
task copy(type: Copy, group: "Custom", description: "Copies sources to the dest directory") {
from "src"
into "dest"
}
vs
tasks.register("gutenTag", Greeting) {
group = 'Welcome'
description = 'Produces a German greeting'
message = 'Guten Tag'
recipient = 'Welt'
}
The first is the (one of the) older methods of adding a task to a build
The second is using register, which enables task configuration avoidance
https://docs.gradle.org/current/userguide/task_configuration_avoidance.html
That is; the task is only configured if the task is used in the build

Alternative to calling a task from within a task, Gradle

I have a custom Gradle plugin that will generate Java files from a template file. I have several such template files in different locations, and I need to "compile" all of them to generate the Java files I need. Once I have the files, I want to package them into a .jar.
My first thought was that I'd execute the "compile" task from the custom plugin from within other tasks. Something like:
task compileFromLocationA <<{
compileTemplate.execute()...
}
task compileFromLocationB
...
packageJar(depends: compileFromLocationA, compileFromLocationB)
...
However, you can't programmatically call a task from within another task. I've heard I might be able to get around this using dependsOn or finalizedBy, but I haven't been able to figure out how to apply those keywords here. What's the "best practice" for something like this? I'm new to Gradle and haven't been able to get very far using the official docs.
You can use dependsOn to accomplish this.
Here is an example:
apply plugin: 'java'
task taskA <<{
println 'task A'
}
task taskB (type:Copy){
println 'B'
}
task taskC (type:Copy){
println 'C'
}
task taskBC (dependsOn:['taskB','taskC'])<<{
println 'BC'
}
taskBC.mustRunAfter taskA
You can also use mustRunAfter to insure a task always runs after another task.
You can have:
1) Some functionality wrapped up in "normal" Groovy functions:
def doSomething() {
// ...
}
task taskA {
description = 'Description for this task'
doLast {
doSomething()
}
}
2) Make use of mustRunAfter between tasks (careful, this can become messy, depending on your config size)
task taskA {
description = 'Description for task A'
doLast {
// ...
}
}
task taskB {
description = 'Description for task B'
doLast {
// ...
}
}
taskB.mustRunAfter taskA
3) Make use of dependsOn between tasks
task taskA {
description = 'Description for task A'
doLast {
// ...
}
}
task taskB(dependsOn: taskA) {
description = 'Description for task B'
doLast {
// ...
}
}

How to order the 'running' of configuration tasks in Gradle?

I've currently got a configuration task being depended on by an execution task that I am calling from command-line, i.e.
task deployTest(dependsOn: [build, assembleForTest]) << {
...
}
This task should essentially grab the files I've assembled in assembleForTest and then deploy them (ssh, etc.)
My assembleForTest code:
task assembleForTest(type: Sync) {
fileMode = 0775
from ("scripts") {
include "**/*.cgi"
filter(org.apache.tools.ant.filters.ReplaceTokens,
tokens: [programName: programName,
version: version,
dbServer: dbServerTest,
deployDir: deployDirTest])
}
from files("scripts/" + programName + ".cgi")
from files("build/libs/" + programName + "-" + version + ".jar")
into ("build/" + programName)
}
But the problem is: My project gets built AFTER this configuration task assembleForTest has run. i.e. it will try build after the assembly is finished, which means an outdated (or nonexistant) deployment is attempted.
Gradle has some of the worst documenting I've seen, I have worked with it for a while and I still don't understand the ideal setup.
Wouldn't that solve your problem?
task assembleForTest(type: Sync, dependsOn: build) {
/* configuration phase, evaluated always and before execution phase of any task */
...
}
task deployTest(dependsOn: assembleForTest) << {
/* execution phase, evaluated only if the task is invoked and after configuration phase for all tasks has been finished */
...
}
EDIT: I added comments within example. Note that the 1st task is provided with configuration while the 2nd is provided with action. The switch is done with left shift operator. Alternative syntax, especially helpful to combine both phases definition, looks as follows:
task plop() {
// some configuration
...
doLast {
// some action
...
}
}
If you put println in place of 'some configuration' it prints always regardless what task is invoked, as that's evaluated in configuration phase.

How do I declare gradle Antlr task output specs to avoid unnecessary rebuilds

I have a typical Antlr 4.5 project with two grammar files: MyLexer.g4 and MyParser.g4. From them, Antlr generates 6 output files: MyLexer.java, MyLexer.tokens, MyParser.java, MyParser.tokens, MyParserBaseListener.java and MyParserListener.java. The gradle tasks are all working correctly so that the output files are all generated, compiled and tested as expected.
The problem is that gradle sees the 6 target files as always being out of date, so every run or debug session has to regenerate them and therefore has to recompile the main java project even if none of the source files have changed.
The gradle task which generates the file has the output spec defined as the folder into which the 6 output files are generated. I think that I need a way to define it as being the 6 specific files rather than the output folder. I just don't know the syntax to do that.
Here's the pertinent part of my build.gradle file:
ext.antlr4 = [
antlrSource: "src/main/antlr",
destinationDir: "src/main/java/com/myantlrquestion/core/antlr/generated",
grammarpackage: "com.myantlrquestion.core.antlr.generated"
]
task makeAntlrOutputDir << {
file(antlr4.destinationDir).mkdirs()
}
task compileAntlrGrammars(type: JavaExec, dependsOn: makeAntlrOutputDir) {
// Grammars are conveniently sorted alphabetically. I assume that will remain true.
// That ensures that files named *Lexer.g4 are listed and therefore processed before the corresponding *Parser.g4
// It matters because the Lexer must be processed first since the Parser needs the .tokens file from the Lexer.
// Also note that the output file naming convention for combined grammars is slightly different from separate Lexer and Parser grammars.
def grammars = fileTree(antlr4.antlrSource).include('**/*.g4')
def target = file("${antlr4.destinationDir}")
inputs.files grammars
// TODO: This output spec is incorrect, so this task is never considered up to date.
// TODO: Tweak the outputs collection so it is correct with combined grammars as well as separate Lexer and Parser grammars.
outputs.dir target
main = 'org.antlr.v4.Tool'
classpath = configurations.antlr4
// Antlr command line args are at https://theantlrguy.atlassian.net/wiki/display/ANTLR4/ANTLR+Tool+Command+Line+Options
args = ["-o", target,
"-lib", target,
//"-listener", //"-listener" is the default
//"-no-visitor", //"-no-visitor" is the default
"-package", antlr4.grammarpackage,
grammars.files
].flatten()
// include optional description and group (shown by ./gradlew tasks command)
description = 'Generates Java sources from ANTLR4 grammars.'
group = 'Build'
}
compileJava {
dependsOn compileAntlrGrammars
// this next line isn't technically needed unless the antlr4.destinationDir is not under buildDir, but it doesn't hurt either
source antlr4.destinationDir
}
task cleanAntlr {
delete antlr4.destinationDir
}
clean.dependsOn cleanAntlr
I discovered the problem was not that the target files were out of date, but rather, due to a bug in the cleanAntlr task, they were being deleted every time that any gradle task was run. The problem was that all of the code in cleanAntlr was being run during gradle's initialization and configuration phase, even if the cleanAntlr task itself wasn't being executed.
Originally, the task was defined as:
task cleanAntlr {
delete antlr4.destinationDir
}
clean.dependsOn cleanAntlr
The solution was to define it like this: (Note the "<<" after the task name.)
task cleanAntlr << {
delete antlr4.destinationDir
}
clean.dependsOn cleanAntlr
... or, for additional clarity, use this more verbose, but functionally equivalent task definition:
task cleanAntlr {
doLast() {
// Be sure to wrap the execution phase code inside doLast().
// Otherwise it will run during the initialization or configuration phase, even when an unrelated task is is run.
// It would also run when the NetBeas IDE first loaded the project.
//println 'Deleting Antlr Directory: ' + antlr4.destinationDir
delete antlr4.destinationDir
}
}
clean.dependsOn cleanAntlr
With that bug fixed, the original outputs specification for the compileAntlrGrammars task works correctly. There is no need to specify each individual output file. This is explained quite well in section 15.9.2 of https://gradle.org/docs/current/userguide/more_about_tasks.html.
def grammars = fileTree(antlr4.antlrSource).include('**/*.g4')
def target = file("${antlr4.destinationDir}")
inputs.files grammars
outputs.dir target
Could you please try the following piece of code:
generatedFiles = ['MyLexer.java',] // and so on..
generatedFiles.each { f -> outputs.file("$target/$f") }

Categories

Resources