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 {
// ...
}
}
Related
this is what the ideal build script I have:
I do want to execute tasks "unzip_natives_os" manually. But it seems it only works at config phase. And when I take a test run with this set-up it gives me an error: "java.lang.UnsatisfiedLinkError" but if I change the configuration from "nativesOS" into "runtimeOnly" inside of the dependencies block, it works fine. Do I have to explicitly create this "applicationDefaultJvmArgs" and insert the libraryPath of the natives. Is there any other way? And when I need to unzip the "nativesOS" config it needs an explicit version, it seems it did not see the platform/BOM?
// build.gradle.kts
val nativesOS : Configuration by configurations.creating {
this.isTransitive = false
this.extendsFrom(configurations.runtimeOnly.get())
}
dependencies {
implementation(platform("org.lwjgl:lwjgl-bom:3.2.3"))
listOf(
"", "-assimp", "-openal",
"-opengl", "-glfw"
).map { lib ->
implementation("org.lwjgl:lwjgl$lib")
// I give it an explicit version, because it would not work if I unzip this.
nativeOS("org.lwjgl","lwjgl$lib", "3.2.3", classifier = LWJGL.lwjglNatives)
}
...
}
// unzip_native_os tasks, here is the problem.
tasks.register<Copy>("unzip_native_os") {
this.group = "zip"
doLast {
nativesOS.asFileTree.filter { it.name.contains("natives") }.forEach {
unzipTo(File("$buildDir/libs/natives-os"), it)
}
}
}
Edited: Why this is not working? I config it first then execute it.
tasks.register<Copy>("unzip_native_os") {
this.group = "zip"
val nativesJar = nativesOS.asFileTree.filter { it.name.contains("natives") }.files
doFirst {
nativesJar.forEach {
println(">>>> $it")
unzipTo(File("$buildDir/libs/natives-os/v2"), it)
}
}
}
Edited: I found a possible answer and it looks promising but I did not implement it yet, because I need some learning to do on building this kind of script plugin/inline plugin. Here's the link: gradle custom task execution phase
Edited: found an alternative/another quick solution here: Fix custom tasks in gradle. Want to run it manually via at Execution Phase
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.
I have a gradle task to zip the .yaml files into a zip folder, which looks like this:
task gradleTask1(type: Zip) {
from 'src/test/resources/source_one/'
include '**/*'
archiveName 'file-collect.zip'
destinationDir(file("${buildDir}/resources/test/staging/target_one"))
}
Similarly, I have other tasks that look the same, but the source and target directories are different.
task gradleTask2(type: Zip) {
from 'src/test/resources/source_two/'
include '**/*'
archiveName 'file-collect.zip'
destinationDir(file("${buildDir}/resources/test/staging/target_two"))
}
task gradleTask3(type: Zip) {
from 'src/test/resources/source_three/'
include '**/*'
archiveName 'file-collect.zip'
destinationDir(file("${buildDir}/resources/test/staging/target_three"))
}
And the main issue is I have to add dependency every time I add a new zip task as follows:
compileJava.finalizedBy gradleTask1
compileJava.finalizedBy gradleTask2
compileJava.finalizedBy gradleTask3
Is there any way I can achieve these steps dynamically? Can I have a single zip task (something like zipAll) and finally the task dependency can be
compileJava.finalizedBy zipAll
Consider the following (example here):
tasks.withType(Zip).all { task ->
def taskName = task.name
if (taskName ==~ /gradleTask.*/) {
println "TRACER adding dependency on ${taskName}"
compileJava.finalizedBy taskName
}
}
This will dynamically find tasks of type Zip with name matching gradleTask* and add it to the list of tasks for compileJava.finalizedBy.
There is no zipAll task, but gradle compileJava will work as desired.
I have a gradle project with the following structure:
rootDir
|--agent-v1.0.0
|--agent.jar
|--proj1
|-- // other project files
|--build.gradle
|--proj2
|-- // other project files
|--build.gradle
|--build.gradle
I would like to run test.jvmArgs = ['javaagent:agent-v1.0.0/agent.jar'] for all subprojects, so I wrote the following task in the root build.gradle:
subprojects {
task cs {
outputs.upToDateWhen { false }
dependsOn test.jvmArgs = ['javaagent:../agent-v1.0.0/agent.jar']
}
}
But this fails with:
Could not determine the dependencies of task ':proj1'.
Task with path 'javaagent:../agent-v1.0.0/agent.jar' not found in project ':proj1'.
I've tried this by putting the agent-v1.0.0 in both the root, and in each project, and it still fails. What am I missing?
Why are you wrapping that logic in a new task? And then passing the return from jvmArgs to dependsOn?
Just configure the test tasks correctly:
subprojects {
tasks.withType(Test) {
jvmArgs "-javaagent:${project.rootDir}/agent-v1.0.0/agent.jar"
}
}
A task can depend on another task. So dependsOn expects a task as argument. test.jvmArgs = ['javaagent:../agent-v1.0.0/agent.jar'] is not a task.
If you want to configure all the test tasks of all subprojects to have additional jvm args, then the syntax would be
subprojects {
// this block of code runs for every subproject
afterEvaluate {
// this block of code runs after the subproject has been evaluated, and thus after
// the test task has been added by the subproject build script
test {
// this block of code is used to configure the test task of the subproject
// this configures the jvmArgs property of the test task
jvmArgs = ['javaagent:../agent-v1.0.0/agent.jar']
}
}
}
But just don't copy and paste this code. Read the grade user guide, and learn its fundamental concepts.
I try to build jar and after that copy it to another folder.
task createJar(type: Jar) {
archiveName = "GradleJarProject.jar"
manifest {
attributes 'Implementation-Title': 'Gradle Jar File Example',
'Implementation-Version': version,
'Main-Class': 'me.test.Test'
}
baseName = project.name
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}
task copyJarToBin {
copy {
from 'build/libs/GradleJarProject.jar'
into "d:/tmp"
}
}
task buildApp (dependsOn: [clean, createJar, copyJarToBin])
But I can't figure out one problem.
copyJarToBin task try to copy old jar. If I delete /build folder in the project and run buildApp() task, task createJar() will generate .jar file, but copyJarToBin() won't find that .jar file.
Could you help me?
Thanks.
The culprit is your copyJarToBin task. when doing
task copyJarToBin {
copy {
from 'build/libs/GradleJarProject.jar'
into "d:/tmp"
}
}
you copy the jar during the configuration time by using the copy method. (see the gradle user guide at https://docs.gradle.org/current/userguide/userguide_single.html#sec:build_phases to understand the build lifecycle)
You want to run the actual copy operation during the execution phase (the execution of the task).
One way to solve that is to move the call of the copy method into a doLast block:
task copyJarToBin {
doLast {
copy {
from 'build/libs/GradleJarProject.jar'
into "d:/tmp"
}
}
}
The problem with this approach is that you won't benefit of gradles incremental build feature and copy that file every single time you execute the task even though the file hasn't changed.
A better and more idiomatic way of writing your copyJarToBin task is to change your task implementation to use the Copy task type:
task copyJarToBin(type: Copy) {
from 'build/libs/GradleJarProject.jar'
into "d:/tmp"
}
We can even improve this snippet by taking advantage of gradle's autowiring feature. You can declare the output of one task as input to another. So instead of writing `build/libs/GradleJarProject.jar' you can simply do:
task copyJarToBin(type: Copy) {
from createJar // shortcut for createJar.outputs.files
into "d:/tmp"
}
Now you don't need to bother about task ordering as gradle know that the createJar task must be executed before the copyJarToBin task can be executed.
I think the above answer is somehow old. Here is an answer using gradle 3.3
jar {
baseName = 'my-app-name'
version = '0.0.1'
}
task copyJar(type: Copy) {
from jar // here it automatically reads jar file produced from jar task
into 'destination-folder'
}
build.dependsOn copyJar
Just made few corrections to above Answers:
jar {
baseName = "$artifactId"
version = '0.0.1'
}
task copyJar(type: Copy) {
from jar // copies output of file produced from jar task
into 'destination-folder'
}
build.finalizedBy copyJar
You probably need to ensure they are run in the right order,
task copyJarToBin(type:Copy,dependsOn:[createJar]) {
copy {
from "${buildDir}/GradleJarProject.jar" // needs to be gstring
into "d:/tmp"
}
}
In my use case I needed projectA to consume (unzip) contents from projectB's jar output. In this case the embedded doLast { copy { .. }} was required.
configurations {
consumeJarContents
}
dependencies {
consumeJarContents project(':projectB')
}
task copyFromJar() {
dependsOn configurations.consumeJarContents
doLast {
copy {
configurations.consumeJarContents.asFileTree.each {
from( zipTree(it) )
}
into "$buildDir/files"
}
}
}
The problem with task copyFromJar(type: Copy) in this scenario is that the Copy configuration phase checks for the jar file, which it does not find because it has not yet been created, and so declares NO-INPUT for the task at execution time.