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.
Related
I have created a task in Gradle as follows:
task findTaskCreatingSpecificOutput() {
dependsOn testClasses
doLast {
tasks.findAll { task ->
task.outputs.getFiles().getFiles().each { output ->
if (output != null && output.getAbsolutePath().contains('generated')) {
println task
println output
}
}
}
}
}
to try and find which two tasks are writing to the same generated location. However the output is a list of tasks and directories which do not overlap at all so it should be easy for Gradle to know exactly which task created the location.
Example output of ./gradlew clean findTaskCreatingSpecificOutput --info
/Users/mylocation/shared/build/generated/sources/annotationProcessor/java/main
task ':shared:compileJava'
/Users/mylocation/shared/build/generated/sources/headers/java/main
task ':shared:compileTestFixturesJava'
/Users/mylocation/shared/build/generated/sources/annotationProcessor/java/testFixtures
task ':shared:compileTestFixturesJava'
/Users/mylocation/shared/build/generated/sources/headers/java/testFixtures
task ':shared:compileTestJava'
/Users/mylocation/shared/build/generated/sources/annotationProcessor/java/test
task ':shared:compileTestJava'
/Users/mylocation/shared/build/generated/sources/headers/java/test
task ':shared:delombok'
/Users/mylocation/shared/build/generated/sources/delombok/java/main
task ':shared:delombokTest'
/Users/mylocation/shared/build/generated/sources/delombok/java/test
task ':shared:delombokTestFixtures'
/Users/mylocation/shared/build/generated/sources/delombok/java/testFixtures
However I still get the warning Gradle does not know how file 'build/classes/java/main/generated' was created (output property 'destinationDirectory'). Task output caching requires exclusive access to output paths to guarantee correctness (i.e. multiple tasks are not allowed to produce output in the same location).
I don't think there's any way to find out which task actually wrote any particular file. The code I used only lists the declared outputs.
In my case the problem was that I was running a bytecode weaving task in doLast for compileJava.
I've changed compileJava to write to a new directory using destinationDirectory and then the weaving task writes the updated classes to sourceSets.main.output.classesDirs.singleFile
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."
}
}
build.gradle is:
task copy(type: Copy, group: "Custom", description: "Copies sources to the dest directory") {
from "src"
into "dest"
println "copy"
}
task hello {
println "hello"
}
And when I try with ./gradlew copy, both copy and hello are executed. Output like below:
> Configure project :
copy
hello
How can I only execute copy?
In your example, only the copy task is executed. However, both are configured.
A Gradle build has three distinct phases in the build lifecycles:
An initialization phase
A configuration phase
An execution phase
By default, Gradle configures all tasks at start-up, though many types of configurations can be deferred (lazy configuration).
The println statement you have in the hello task is part of the configuration and this is why you see it no matter what task you intend to execute. You can also see in the output that is under the > Configure project : header.
If you want to only print "hello" when it actually executes, move it into a doLast block like this:
task hello {
doLast {
println "hello"
}
}
I'm very new to gradle and have a basic question.
When I add a custom task to my gradle.build file and call "gradlw build" or "gradle clean" or any other gradle command,
it automatically runs my custom task.
Is that how things work in gradle? run every task in the build file?
Is there way to run a task only when I want it manually?
task foo {
println 'hello'
}
That creates a task, and during the configuration of the task, it tells gradle to execute println 'hello'. Every task is configured at each build, because gradle needs to know what its configuration is to know if the task must be executed or not.
task foo << {
println 'hello'
}
That creates a task, and during the execution of the task, it tells gradle to execute println 'hello'. So the code will only be executed if you explicitly chose to run the task foo, or a task that depends on foo.
It's equivalent to
task foo {
doLast {
println 'hello'
}
}
You chose not to post your code, probably assuming that gradle was acting bizarrely, and that your code had nothing to do with the problem. So this is just a guess, but you probably used the first incorrect code rather than the second, correct one.
I've prepared a very simple script, that illustrates the problem I see using Gradle 1.7 (need to stick with it because of some plugins not yet supporting newer versions).
I'm trying to dynamically create tasks each of which corresponds to a file in the project directory. This works fine, but the tasks I create never get executed as soon as I assign them type 'Copy'.
Here is my problem build.gradle:
file('templates').listFiles().each { File f ->
// THIS LINE DOES NOT WORK
task "myDist-${f.name}" (type: Copy) {
// NEXT LINE WORKS
//task "myDist-${f.name}" {
doLast {
println "MYDIST-" + f.name
}
}
}
task distAll(dependsOn: tasks.matching { Task task -> task.name.startsWith("myDist")}) {
println "MYDISTALL"
}
defaultTasks 'distAll'
in this way my tasks do not get executed when I call default task calling simply gradle:
MYDISTALL
:myDist-template1 UP-TO-DATE
:myDist-template2 UP-TO-DATE
:distAll UP-TO-DATE
BUILD SUCCESSFUL
If I remove type Copy from my dynamic task (uncommenting the line above), my tasks get executed:
MYDISTALL
:myDist-template1
MYDIST-template1
:myDist-template2
MYDIST-template2
:distAll
BUILD SUCCESSFUL
(You'll need to create a folder name templates in the same directory where build.gradle is located and put couple of empty files into there in order to run the test)
According to the debug output:
Skipping task ':myDist-template1' as it has no source files.
Skipping task ':myDist-template2' as it has no source files.
So how can I specify source files and make my Copy tasks execute?
I've tried adding
from( '/absolute/path/to/existing/file' ) {
into 'myfolder'
}
to the task body, I've tried assigning task's inputs.source file('/my/existing/file') with no success.
Could you please advise on how to modify my simple script leaving dynamic task creation and keeping my dynamic tasks of type Copy?
Thank you!
Edit:
All right, this way the task gets called:
file('templates').listFiles().each { File f ->
task "myDist-${f.name}" (type: Copy) {
from f
into 'dist'
doLast {
println "MYDIST-" + f.name
}
}
}
but it looks I must always specify from/into. It doesn't suffice to do that in the doLast{} body.
A Copy task only gets executed if it has something to copy. Telling it what to copy is part of configuring the task, and therefore needs to be done in the configuration phase, rather than the execution phase. These are very important concepts to understand, and you can read up on them in the Gradle User Guide or on the Gradle Forums.
doFirst and doLast blocks get executed in the execution phase, as part of executing the task. Both are too late to tell the task what to copy: doFirst gets executed immediately before the main task action (which in this case is the copying), but (shortly) after the skipped and up-to-date checks (which are based on the task's configuration). doLast gets executed after the main task action, and is therefore clearly too late.
I think the following Gradle User Guide quote answers my question the best:
Secondly, the copy() method can not honor task dependencies when a task is used as a copy source (i.e. as an argument to from()) because it's a method and not a task. As such, if you are using the copy() method as part of a task action, you must explicitly declare all inputs and outputs in order to get the correct behavior.
Having read most of the answers to "UP-TO-DATE" Copy tasks in gradle, it appears that the missing part is 'include' keyword:
task copy3rdPartyLibs(type: Copy) {
from 'src/main/jni/libs/'
into 'src/main/libs/armeabi/'
include '**/*.so'
}
Putting from and into as part of the doLast section does not work. An example of a working task definitions is:
task copyMyFile(type: Copy) {
def dockerFile = 'src/main/docker/Dockerfile'
def copyTo = 'build/docker'
from dockerFile
into copyTo
doLast {
println "Copied Docker file [$dockerFile] to [$copyTo]"
}
}
Not the behavior I was expecting.
Using gradle 3.2.1