Gradle, dependsOn ordering - gradle

Confused by ordering in Gradle. I'm a novice, previously used Ant for builds, having a play with Gradle which may explain some of this
Brief background (in case anyone would be asking "why would you do that").
We ship a Java WebStart app in a war file. Java 7 (-40 onwards) is prompting users about incorrectly formatted jar files with missing elements. I would like to automate a process that gets the war; extracts the jars for JavaWS from 'bin' folder; adds elements to the Manifest
Later I'll get to re-signing the jars and re-assembling the war, but for now I just want to get to adding the Manifest entries.
My problem is that I have defined tasks that have dependsOn elements but the tasks seem to run in the wrong order (see gradle file below).
What I expect is the tasks to run in sequence: delete dir; get war; unwar the war; add elements to the jar.
What I see (from log file) is: elements are added to jar, effectively this creates a new jar, the dir is deleted and the war is unwar'ed so I end up with the original war file contents.
I checked: Gradle Task To Call Other Tasks In Order however, I don't seem able to use mustRunAfter at all, could be related to version of gradle I have, but anyway I don't really want to control order of disparate tasks, I would prefer (in my Ant based thinking) that I can define the order I want by having tasks depend on each other.
Can anyone see the flaw in my build file?
NOTE: if I remove the "dependsOn" from the resignclientjars task and run it manually after running the other tasks, it all works fine and I get my jars with the new elements in the Manifest, so I have a workable workaround, but would prefer to know what I'm doing wrong here.
task (deletework, type: Delete) {
delete 'workYYY'
}
task (getlaganwar, type: Copy, dependsOn: deletework) {
from "d:/dev/v8-0-5/wars"
into "workYYY"
include 'lagan.war'
}
task (unwar, type: Copy, dependsOn: getlaganwar) {
from zipTree(file('workYYY/lagan.war'))
into file("workYYY/lagan")
}
task (resignclientjars, type: Copy, dependsOn: unwar) {
//task (resignclientjars, type: Copy) {
// mustRunAfter unwar
def workDir = file("workYYY/lagan")
def binDir = file(new File(workDir, "bin"))
def collection = files { binDir.listFiles() }
collection.each {
File jarFile = new File(binDir, it.name)
ant.echo(message: "updating:${jarFile.absolutePath}")
ant.jar(jarfile: jarFile, update: 'true') {
manifest {
attribute(name: 'Implementation-Title', value: 'Lagan Enterprise')
attribute(name: 'Implementation-Vendor', value: 'Lagan Enterprise')
attribute(name: 'Implementation-Version', value: 'Lagan Enterprise')
attribute(name: 'Application-Name', value: 'Lagan Enterprise')
attribute(name: 'Permissions', value: 'all-permissions')
}
}
}
}
//unwar.mustRunAfter getlaganwar
//getlaganwar.mustRunAfter deletework
//resignclientjars.mustRunAfter getlaganwar
Debug Output contains the following:
[sts] -----------------------------------------------------
[sts] Starting Gradle build for the following tasks:
[sts] :resignclientjars
[sts] -----------------------------------------------------
12:06:19.658 [WARN] [org.gradle.api.internal.project.ant.AntLoggingAdapter] [ant:echo] updating:D:\dev\util\java7-clientjars\workYYY\lagan\bin\DebugWinIEBrowser.jar
12:06:19.666 [WARN] [org.gradle.api.internal.project.ant.AntLoggingAdapter] [ant:echo] updating:D:\dev\util\java7-clientjars\workYYY\lagan\bin\DebugWinMSWord.jar
12:06:19.670 [WARN] [org.gradle.api.internal.project.ant.AntLoggingAdapter] [ant:echo] updating:D:\dev\util\java7-clientjars\workYYY\lagan\bin\WinIEBrowser.jar
12:06:19.674 [WARN] [org.gradle.api.internal.project.ant.AntLoggingAdapter] [ant:echo] updating:D:\dev\util\java7-clientjars\workYYY\lagan\bin\WinMSWord.jar
12:06:19.690 [INFO] [org.gradle.execution.TaskNameResolvingBuildConfigurationAction] Selected primary task ':resignclientjars'
12:06:19.692 [DEBUG] [org.gradle.cache.internal.DefaultFileLockManager] Waiting to acquire exclusive lock on task artifact state cache (D:\dev\util\java7-clientjars\.gradle\1.5\taskArtifacts).
12:06:19.692 [DEBUG] [org.gradle.cache.internal.DefaultFileLockManager] Lock acquired.
12:06:19.692 [INFO] [org.gradle.BuildLogger] Tasks to be executed: [task ':deletework', task ':getlaganwar', task ':unwar', task ':resignclientjars']
12:06:19.693 [DEBUG] [org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter] Starting to execute task ':deletework'
12:06:19.693 [DEBUG] [org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter] Determining if task ':deletework' is up-to-date
12:06:19.694 [INFO] [org.gradle.api.internal.changedetection.ShortCircuitTaskArtifactStateRepository] Task ':deletework' has not declared any outputs, assuming that it is out-of-date.
12:06:19.694 [DEBUG] [org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter] task ':deletework' is not up-to-date
12:06:19.695 [DEBUG] [org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter] Executing actions for task ':deletework'.
12:06:19.695 [DEBUG] [org.gradle.api.internal.file.copy.DeleteActionImpl] Deleting D:\dev\util\java7-clientjars\workYYY
12:06:19.934 [DEBUG] [org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter] Finished executing task ':deletework'
12:06:19.934 [LIFECYCLE] [org.gradle.TaskExecutionLogger] :getlaganwar
12:06:19.934 [DEBUG] [org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter] Starting to execute task ':getlaganwar'
12:06:19.936 [DEBUG] [org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter] Determining if task ':getlaganwar' is up-to-date
12:06:19.942 [INFO] [org.gradle.api.internal.changedetection.DefaultTaskArtifactStateRepository] Executing task ':getlaganwar' due to:
Output file D:\dev\util\java7-clientjars\workYYY for task ':getlaganwar' has changed.
Output file D:\dev\util\java7-clientjars\workYYY\lagan.war has been removed for task ':getlaganwar'.
12:06:19.942 [DEBUG] [org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter] task ':getlaganwar' is not up-to-date
12:06:19.944 [DEBUG] [org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter] Executing actions for task ':getlaganwar'.
12:06:20.564 [DEBUG] [org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter] Finished executing task ':getlaganwar'
12:06:20.565 [LIFECYCLE] [org.gradle.TaskExecutionLogger] :unwar
12:06:20.565 [DEBUG] [org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter] Starting to execute task ':unwar'
12:06:20.586 [DEBUG] [org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter] Determining if task ':unwar' is up-to-date
12:06:20.588 [DEBUG] [org.gradle.api.internal.changedetection.DefaultFileCacheListener] Can cache files for ZIP 'D:\dev\util\java7-clientjars\workYYY\lagan.war'
12:06:20.588 [DEBUG] [org.gradle.api.internal.changedetection.DefaultFileCacheListener] Can cache files for file 'D:\dev\util\java7-clientjars\workYYY\lagan'
12:06:24.096 [INFO] [org.gradle.api.internal.changedetection.DefaultTaskArtifactStateRepository] Executing task ':unwar' due to:
Output file D:\dev\util\java7-clientjars\workYYY\lagan for task ':unwar' has changed.
12:06:24.097 [DEBUG] [org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter] task ':unwar' is not up-to-date
12:06:24.100 [DEBUG] [org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter] Executing actions for task ':unwar'.
12:06:27.863 [DEBUG] [org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter] Finished executing task ':unwar'
12:06:27.863 [LIFECYCLE] [org.gradle.TaskExecutionLogger] :resignclientjars
12:06:27.864 [DEBUG] [org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter] Starting to execute task ':resignclientjars'
12:06:27.864 [INFO] [org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter] Skipping task ':resignclientjars' as it has no source files.
12:06:27.864 [DEBUG] [org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter] Finished executing task ':resignclientjars'
12:06:27.864 [LIFECYCLE] [org.gradle.TaskExecutionLogger] :resignclientjars UP-TO-DATE
12:06:27.865 [DEBUG] [org.gradle.execution.taskgraph.DefaultTaskGraphExecuter] Timing: Executing the DAG took 8.173 secs
12:06:27.865 [LIFECYCLE] [org.gradle.BuildResultLogger]
12:06:27.865 [LIFECYCLE] [org.gradle.BuildResultLogger] BUILD SUCCESSFUL
12:06:27.865 [LIFECYCLE] [org.gradle.BuildResultLogger]
12:06:27.866 [LIFECYCLE] [org.gradle.BuildResultLogger] Total time: 8.886 secs

The unpredictable dependency ordering in Gradle is really annoying.
Here is workaround for executing the dependent tasks in order:
task dist(type: Zip) {
def tasks = [clean, jar, test, docs]
for (int i = 0; i < tasks.size() - 1; i++) {
tasks[i + 1].mustRunAfter(tasks[i])
}
dependsOn(tasks)
//...other stuff
}
Probably this workaround could be extracted in reusable manner as strictDependsOn()...

You're using Gradle 1.5, and mustRunAfter dates from 1.6. The current version is 1.8.
dependsOn doesn't fix any order for the tasks. This has been discussed ad nauseam in various bug reports. The workaround is to use depencencies between indifidual tasks, or mustRunAfter.

The problem with your build script isn't the task dependencies, but that the task definition for resignclientjars is incorrect. It's doing its work in the configuration phase (i.e. for every build invocation whatsoever) instead of the execution phase. A correct task definition would look as follows:
task resignclientjars(dependsOn: unwar) {
doLast {
...
}
}
You can read up on configuration phase vs. execution phase in the Gradle User Guide or the Gradle forums.

For whatever reason gradle does guarantee order for dependsOn, after time they added mustRunAfter, which however you have to chain yourself like a idiot, so here is a utility:
task buildAppRelease() {
group = "build"
dependsOn ordered(":allClean", ":allTestReleaseUnitTest", ":app:assembleRelease")
}
def ordered(String... dependencyPaths) {
def dependencies = dependencyPaths.collect { tasks.getByPath(it) }
for (int i = 0; i < dependencies.size() - 1; i++) {
dependencies[i + 1].mustRunAfter(dependencies[i])
}
return dependencies
}

I found that it works if I use both dependsOn and mustRunAfter. Here is an example of running two tasks, one (custom registered "importUnicodeFiles" task) that is in "this" project and one (predefined "run" task) that is in a sibling project named ":unicode":
tasks.register("rebuildUnicodeFiles") {
description = "Force the rebuild of the `./src/main/resources/text` data"
val make = project(":unicode").tasks["run"]
val copy = tasks["importUnicodeFiles"]
dependsOn(make)
dependsOn(copy)
copy.mustRunAfter(make)
}

I have been able to use dependsOn and mustRunAfter as below to define order for the tasks. Note, here I want to run ktlintFormat first then run ktlint.
ktlint.mustRunAfter ktlintFormat
compileKotlin.dependsOn ktlintFormat,ktlint

Related

How to diagnose / troubleshoot gradle build not running dependency task?

I'm adding gradle to a multi-project build and hitting the learning curve.
I need to invoke a custom task of type:Exec before compilation of a subproject. The task is not invoked. Why?
build.gradle of parent project
task precompiletask(type:Exec) {
println "Executing pre-compile task"
// ...
}
task(":cppproj:build").dependsOn precompiletask
// Also tried this, same output
//project(":cppproj").task(":build").dependsOn precompiletask
// Also tried this -> error "Cannot add task 'build' as a task with that name already exists"; why on Earth would this syntax *add* the task "build"?
//project(":cppproj").task("build").dependsOn precompiletask // I also tried this
build.gradle of subproject 'cppproj'
apply plugin: "cpp"
model {
components {
api(NativeLibrarySpec) {
sources {
// ...
}
}
}
}
-
Related questions
Why do I find it so hard to debug this? I am running gradle with the verbose-est output (gradle build --debug --warning-mode all). The only mentions of my custom task precompiletask are these, clustered towards the beginning of the output:
07:24:41.151 [DEBUG] [org.gradle.internal.operations.DefaultBuildOperationExecutor] Build operation 'Realize task :precompiletask' started
07:24:41.243 [DEBUG] [org.gradle.internal.operations.DefaultBuildOperationExecutor] Completing Build operation 'Realize task :precompiletask'
07:24:41.243 [DEBUG] [org.gradle.internal.operations.DefaultBuildOperationExecutor] Build operation 'Realize task :precompiletask' completed
07:24:41.249 [QUIET] [system.out] Executing pre-compile task
Why no errors related to failing to link the two tasks using dependsOn? Since dependsOn is clearly invoked in my code I'd expect an error that either the subproject or tasks are not found, or dependsOn itself fails somehow. Does this have to do with gradle's flexibility of being able to refer to stuff that doesn't exist yet?
What does it mean to 'Realize task' anyway? Could not find this documented. Thanks.
You are not using the proper way to access the :cppproj:build task in your root project build: you are using project.task() methods , which is actually creating a new task.
You have several ways available to 'locate' the task you want to configure (adding the dependsOn constraint), as explained here : locating tasks
In your case you could write:
tasks.getByPath(':cppproj:build').dependsOn precompiletask
Note 1:
When using the syntax task(":cppproj:build").dependsOn precompiletask: you create a new task named ':cppproj:build' on the root project and make it depend on precompiletask: that's why precompiletask is not executed if you execute build tasks from parent or subproject.
Note 2:
// Also tried this -> error "Cannot add task 'build' as a task with that name already exists"; why on Earth would this syntax add the task "build"?
//project(":cppproj").task("build").dependsOn precompiletask
=> because the project.task(String) method creates a task, so you are trying to add new task named build to the subproject which already have one build task.
Just adding an answer to my sequel question: why does this seemingly equivalent syntax fail to achieve the same thing: project("cppproj").tasks["build"].dependsOn.
I found that the "configuration" step of gradle build is done by default in a top-down manner, meaning when the root project build.gradle is executed, the subprojects don't contain their tasks yet. This can be changed by doing:
evaluationDependsOnChildren()
... and then one can write:
subprojects.each {
it.tasks["build"].dependsOn myPreCompileTask
}

Task of type Javadoc is always up to date

I have a task of type Javadoc like this:
class CreateJavadocsTask extends Javadoc{
#TaskAction
def action1()
{
project.source = project.android.sourceSets.main.java.srcDirs
options.linkSource true
classpath += project.files(project.android.getBootClasspath().join(File.pathSeparator))
failOnError false
}
}
This task gives an error that task is up to date as it has no source files:
Baloe is the stacktrace :
Using incremental javac compilation.
Incremental java compilation is an incubating feature.
Not using incremental javac compilation.
Using incremental javac compilation.
Not using incremental javac compilation.
Using incremental javac compilation.
All projects evaluated.
[buildinfo] Not using buildInfo properties file for this build.
Selected primary task 'createJavadocs12' from project :
Tasks to be executed: [task ':bluetooth:createJavadocs12']
:bluetooth:createJavadocs12 (Thread[main,5,main]) started.
:bluetooth:createJavadocs12
Skipping task ':bluetooth:createJavadocs12' as it has no source files.
:bluetooth:createJavadocs12 UP-TO-DATE
:bluetooth:createJavadocs12 (Thread[main,5,main]) completed. Took 0.034 secs.
You are assigning the inputs of the task during execution time. You will need to declare the inputs outside of the task action. Otherwise, Gradle won't be able to determine inputs/outputs of the task during configuration time and therefore mark it up-to-date.

Gradle Task Execution Skipped after commandLine in doFirst

This is a build process on Windows.
I have gradle task (type: Exec) which uses its doFirst closure to temporarily copy a file from a foreign directory, which is required for its build process:
task(myTask, type: Exec) {
[...]
< do own stuff >
[...]
doFirst {
println "Copy file"
if (System.properties['os.name'].toLowerCase().contains('windows')) {
commandLine 'cmd', '/c', "copy .\\<myFile> .\\..\\.."
}
}
}
For some reason, using commandLine within doFirst, to execute the copy command, makes the whole task terminate and prevents the execution of the task's main purpose.
Using switch --debug I took a look into the debug output and saw that the copy command makes the state to be changed to succeeded:
[DEBUG] [org.gradle.process.internal.DefaultExecHandle] Changing state to: STARTING
[DEBUG] [org.gradle.process.internal.DefaultExecHandle] Waiting until process started: command 'cmd'.
[DEBUG] [org.gradle.process.internal.DefaultExecHandle] Changing state to: STARTED
[DEBUG] [org.gradle.process.internal.ExecHandleRunner] waiting until streams are handled...
[INFO] [org.gradle.process.internal.DefaultExecHandle] Successfully started process 'command 'cmd''
[QUIET] [system.out] 1 file(s) copied.
[DEBUG] [org.gradle.process.internal.DefaultExecHandle] Changing state to: SUCCEEDED
[DEBUG] [org.gradle.process.internal.DefaultExecHandle] Process 'command 'cmd'' finished with exit value 0 (state: SUCCEEDED)
[DEBUG] [org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter] Finished executing task 'myTask'
How can I prevent this "commandLine" within doFirst to stop the task further execution?
Instead of commandLine in doFirst closure just use plain old copy block:
task(myTask, type: Exec) {
[...]
< do own stuff >
[...]
doFirst {
println "Copy file"
copy {
from "some file"
into "some path"
}
}
}

Create Gradle task that contains several tasks

Is it possible to create a gradle task that runs several tasks? My goal would be to to have a command cleanAndTestAll that would be executed like:
./gradlew cleanAndTestAll
and would be the equivalent of doing:
./gradlew clean :unit:test :app:connectedAndroidTestPlayDebug
One way is to define a wrapper task that depends on the tasks you want to run.
For example adding the following to the root build.gradle :
task cleanAndTestAll(dependsOn: [ clean, ':unit:test', ':app:connectedAndroidTestPlayDebug']) { }
This task will trigger the two other tasks. and give output like the following:
15:31:38: Executing external task 'cleanAndTestAll'...
:clean
:app:connectedAndroidTestPlayDebug
:unit:test
:cleanAndTestAll
BUILD SUCCESSFUL
If you want to enforce an ordering between the tasks, you could do something like:
task cleanAndTestAll(dependsOn: [clean, ':unit:test', ':app:connectedAndroidTestPlayDebug']) { }
tasks.getByPath(':app:connectedAndroidTestPlayDebug').mustRunAfter tasks.getByPath(':unit:test')
Find out more about gradle tasks at:
https://docs.gradle.org/current/userguide/more_about_tasks.html

Why gradle clean task starts all other non-default tasks?

I have gradle set up and running. My build.gradle has 2 tasks defined inside:
task setVersion() {
println('setVersion')
//...
}
task setIntegrationEnv() {
println('setIntegrationEnv')
//...
}
When I run
./gradlew clean
gradle runs both tasks setVersion and setIntegrationEnv and then it runs clean for all my modules (app, cloud_module) in that project, output:
Relying on packaging to define the extension of the main artifact has been deprecated and is scheduled to be removed in Gradle 2.0
setVersion
setIntegrationEnv
:cloud_module:clean
:app:clean
BUILD SUCCESSFUL
Total time: 14.18 secs
Why this happens, where this behavior is defined?
Could You please provide full build.gradle script? I'd be much easier to help You. You've probably mistaken gradle build phase with configuration phase - it's a common topic here.
General rule is that code You'd like to be run at build phase should be added as an action:
task someTask << {
println 'runtime'
}
while code You'd like to run at configuration phase should be added in task body:
task someTask {
println 'configuration
}
or all together:
task someTask {
println 'configuration'
doLast {
println 'runtime'
}
}
Additional info can be found here, here and here.

Resources