Gradle Task Execution Skipped after commandLine in doFirst - windows

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"
}
}
}

Related

Issue with Exec task in Gradle

I am trying to create Exec task using Gradle as shown below.
task clean(type:Exec) {
doFirst {
println 'Cleaning the existing class files ...'
}
workingDir './bin'
commandLine 'del', 'app\\*.class'
}
When I execute it, I am getting the below error.
Task :clean FAILED
Cleaning the existing class files ...
FAILURE: Build failed with an exception.
What went wrong:
Execution failed for task ':clean'.
A problem occurred starting process 'command 'del''
Also, I try to compile java files using Exec in Gradle with command lines but somehow, the javac command is getting invoked and hence PATH variable declared in the system is working but the CLASSPATH set in the system is not getting used due to which I am getting class not found exception as shown below.
task build_2(type:Exec, dependsOn: [clean]) {
doFirst {
println 'Compiling ...'
}
workingDir './src'
commandLine 'javac', 'app/MySQLTester.java'
}
Note that whatever I enter in the commandLine of the Exec task works perfectly in Command Prompt from the workingDir I have mentioned in the Gradle task for both del and javac.
Kindly note that I am experimenting with Gradle Exec and hence not using actual Java gradle plugins.

Gradle Copy task doesn't work with doFirst / doLast

I have a simple copy task that is successfully copying files, but not executing the doFirst/doLast items configured for the task.
Here's the code (build.gradle):
task x(type: Copy) {
copy {
from(projectDir) {
include 'build.gradle'
}
into(buildDir)
}
println "Configuring."
doFirst {
println "Inside doFirst."
}
doLast {
println "Inside doLast."
}
}
Simple enough: The single task 'x' copies the build.gradle script itself - the sole file in the project folder - into the project's build folder.
Here's execution, first ensuring there is no prior output and showing that there is an output after execution:
c:\jdev\newpaas\gradlebug>rd /s /q build & gradle x & dir build
The system cannot find the file specified.
> Configure project :
Configuring.
BUILD SUCCESSFUL in 1s
Volume in drive C is Windows
Volume Serial Number is 22A1-4AC1
Directory of c:\jdev\newpaas\gradlebug\build
01/05/2022 07:44 PM <DIR> .
01/05/2022 07:44 PM <DIR> ..
01/05/2022 07:44 PM 277 build.gradle
1 File(s) 277 bytes
2 Dir(s) 31,110,651,904 bytes free
First, the build folder was not present - hence it could not be deleted.
Gradle is showing the message in the configuration phase. But the "dofirst" and "doLast" messages are not shown.
After execution, the build folder does exist and is properly populated with a copy of the build script, suggesting that the 'x' task did in fact execute. But the messages in doFirst and doLast did not print.
There are similar questions on StackOverflow and elsewhere, but the examples I've seen have been flawed in that the code shown in those questions has shown no inputs or outputs - just doFirst and doLast - and so the answer has been that the task is already up to date and that is why it is not executing; or that the entire body of the task is preceded by '<<' and is therefore an empty task with a doLast block. This is an example of a Copy task that is configured, **is ** demonstrably executing, just not running its doFirst and doLast closures.
I am running Gradle 7.3 on Windows 10.
My need is not to print messages, but I have discovered that a project plugin's post-task configuration is not executing in Copy tasks, and it appears to boil down to this issue. I have tried using 'into('') (path to file, instead of path to folder with includes), and adding an outputs.upToDateWhen { false } entry in the task definition.
You don't have to call copy again inside your custom task. try below
task x(type: Copy) {
from(projectDir) {
include 'build.gradle'
}
into(buildDir)
println "Configuring " + projectDir
doFirst {
println "Inside doFirst."
}
doLast {
println "Inside doLast."
}
}

Gradle copy task not finding source files when leftShift (<<) operator used

I have the following gradle task in a subproject's build.gradle file to copy file.txt from a directory called from_dir/ to a directory to_dir/ and rename it to fileRenamed.txt:
task copyRenameFile(type: Copy) {
System.out.println("copyRenameFile begin")
from('from_dir')
into('to_dir')
include('file.txt')
rename('file.txt', 'fileRenamed.txt')
System.out.println("copyRenameFile end")
}
build.finalizedBy(copyRenameFile)
When I run gradlew :subprojectname:build, this task performs the copy as expected after the build task is finalized, but it executes the printlns during the configuration phase, before the build task.
In an attempt to make the printlns appear after the build phase when the copy is actually being executed, I tried using the << operator like so:
task copyRenameFile(type: Copy) << {
But this results in the task beings skipped with the following message:
[INFO] [org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter] Skipping task ':subprojectname:copyRenameFile' as it has no source files.
Does this mean file.txt could not be found during the execution phase? Why?
EDIT: After reading this answer, I now understand that my usage of the << operator is causing the configuration phase to skip this task and this is the reason the source files are not found. So I guess I can't use the << operator, but how else can I make the printlns occur when the task is being executed and not when it is being configured?
I figured it out:
task copyRenameFile(type: Copy) {
doFirst{
System.out.println("copyRenameFile begin")
}
from('from_dir')
into('to_dir')
include('file.txt')
rename('file.txt', 'fileRenamed.txt')
doLast{
System.out.println("copyRenameFile end")
}
}
build.finalizedBy(copyRenameFile)
I got rid of the << and instead used doFirst and doLast to ensure my printlns occured during the execution phase.

Gradle task executes is Configuration phase but not in Execution phase

I have a Gradle task which simply copies the files from one folder to another.
When the copy 'from' 'into' methods are called during configuration phase it works but when they are called in the execution phase it doesn't.
Doing it this way it works
task copyServerConfig(type:Copy) {
from "${projectDir}" + File.separator + 'server_config'
into localServer
}
gradle copyServerConfig
:copyServerConfig
BUILD SUCCESSFUL
Total time: 0.686 secs
With the '<<' it doesn't show any error but the files are not copied:
task copyServerConfig(type:Copy) << {
from "${projectDir}" + File.separator + 'server_config'
into localServer
}
gradle copyServerConfig
:copyServerConfig UP-TO-DATE
BUILD SUCCESSFUL
Total time: 0.654 secs
Same thing happen if I put it inside a doFirst{} block.
Another thing I don't understand is the following:
If I execute this task:
task task1(type:Exec) << {
commandLine 'echo', ' TEST'
}
I get this error:
gradle task1
:task1 FAILED
FAILURE: Build failed with an exception.
What went wrong:
Execution failed for task ':task1'.
execCommand == null!
But if I change to this:
task task1(type:Exec) {
commandLine 'echo', ' TEST'
}
gradle task1
:task1
TEST
BUILD SUCCESSFUL
Total time: 0.655 secs
I read in the documentation about the about build lifecycle and I understand commands inside doFirst{} and doLast{} blocks execute during the Execution phase and commands outside those blocks execute in the Configuration phase.
But I think there is some important concept I am missing here. Why is this happening ? What is the difference of having a command being executed in the Configuration phase vs Execution phase ?
The first variant is a correct way to declare a Copy task
task copyServerConfig(type:Copy) {
from "${projectDir}" + File.separator + 'server_config'
into localServer
}
Here you say "this is a task of type Copy with name copyServerConfig, this is from param and this is into param". The code inside the brackets is used to configure a task.
<< operator adds a doAfter closure to the task, that is executed after it.
task copyServerConfig(type:Copy) << {
from "${projectDir}" + File.separator + 'server_config'
into localServer
}
This declaration means "create empty (not configured) Copy task and execute these two lines after it is done. Here you did not configure a task, it does nothing and is always UP-TO-DATE. The two lines in doAfter closure do nothing as well, because they are not related to Copy task, so from and into keywords have no effect.
The similar thing happens with Exec task
task task1(type:Exec) << {
commandLine 'echo', ' TEST'
}
Here you have empty Exec task that fails because it is not configured.

Gradle, dependsOn ordering

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

Resources