Delete task always UP-TO-DATE - gradle

I've a simple task in Gradle:
task cleanBuild(type: Delete) {
def build = ".src/buildfiles"
FileTree tree = fileTree (dir: dbEditorBuild);
tree.each { File file ->
println file
}
}
When I run it, I get this output:
:user:cleanBuild UP-TO-DATE
BUILD SUCCESSFUL
Total time: 1.656 secs
I've read the docs and it says that tasks results are cached for performance. I wanted to rerun the task, but I couldn't. And that was despite editing the task code. So, apparently, it seems that Gradle is not able to detect that the task has been changed, which kind of suck.
I've tried what others recommended, like adding this line to the task:
outputs.upToDateWhen { false }
But it doesn't have any effect.

You define a task of the type Delete, but you don't define any files to delete. That is the reason, why your task is always up-to-date, since it has nothing to do. You can define which files will be deleted via the delete method. Everything you pass to this method will be evaluated via Project.files(...):
task myDelete(type: Delete) {
delete 'path/to/file', 'path/to/other/file'
}
Please note, that your code example does not interfere with the up-to-date checks, it doesn't even interfere with the task at all. Since you are not using a doFirst/doLast closure, you are using the configuration closure of the task, which is executed during configuration phase. Since you are also not using any task methods, your code would mean absolutely the same if it would be placed outside of the task configuration closure.
As a small addition: Even if this specific problem is not caused by the Gradle up-to-date checks, there is a way to force Gradle to execute all tasks ignoring any task optimization: Simply add --rerun-tasks as command line argument, as described in the docs.

If you are trying to delete some additional files that are not deleted with a default clean task (because it deletes only the build directories) you can extend the clean task to delete other things as well.
clean {
delete += "$buildDir"
delete += "$rootDir/someDir/someClass.java"
delete += "$rootDir/otherDir
}
Or create a new task to delete files and dependOn it to put in the build lifecycle.
task deleteSomething(type: Delete) {
// to delete a file
delete 'uglyFolder', 'uglyFile'
// to delete a directory
delete 'uglyFolder'
followSymlinks = true
}
Be default symlinks will not be followed when deleting files. To change this behavior call Delete.setFollowSymlinks(boolean) with true. On systems that do not support symlinks, this will have no effect.
Or you can put the action into the execution phase, and delete it.
task cleanBuild {
def build = new File("$rootDir/src/buildfiles")
doLast{
build.deleteDir()
}
}
Also, be sure the task has something to do because if the task has nothing to do, there is nothing to delete etc it will print UP-TO-DATE #lu.koerfer answer explains it perfectly.

Related

Gradle not running copy task after jar

I am trying to do something like this:
jar {
doLast{
from "build/libs/TheJar.jar"
into "."
}
}
So far, I have tried various tutorials including all forms from this answer but non have worked. The only thing that works is calling a separate task but I'd like to know why my construction is wrong and why can't I run something after the jar or shadowJar tasks.
It looks like you took some parts of the answers in the linked post and somehow mixed them without knowing what your final code actually does.
Tasks in Gradle may have a type (e.g. Copy, Jar, ...). This type defines what the task will do once it gets executed (so called task actions). A task without a type won't do anything when its executed, unless you add task actions manually. Using doFirst will add the passed action (also called closure) to the start of the list of task actions, using doLast will add it to the end of the list.
Everything outside of doFirst and doLast closures is not part of the execution of the task, but can be used to configure the task:
task example {
doLast {
println "second action"
}
doFirst {
println "first action"
}
println "configuration"
}
Run the code above with gradle example and you will see the order of the log messages as configuration, first action, second action.
Task configuration will run, even if the task won't be executed later on. Even if you call gradle (without any task names as arguments), the console will still print configuration. This was the actual problem in the linked question.
Lets come to the real question: How to copy a file?
Well, Gradle offers two ways to copy a file. The first one is the task type Copy. You can create a task based on this type, apply your configuration and then either call it directly from the command line or define task dependencies using dependsOn or finalizedBy:
task copySomeFiles(type: Copy) {
from '...'
into '...'
}
However, you don't want to create an additional task. Gradle also has a method called copy that may be called anywhere in your script and will instantly copy the files:
copy {
from '...'
into '...'
}
The code above will copy your files, but it will copy your files every time Gradle executes (which may be often, e.g. when using an IDE). To solve this problem, you may move your code with copy to a task action, e.g. inside a doFirst or doLast closure:
jar {
doLast {
copy {
from "build/libs/TheJar.jar"
into "."
}
}
}
As you can see, your code was missing the copy statement. Now, whenever your task jar gets executed, its last task action will copy the files as intended.
Bonus question: Why is there no error?
The "problem" in your case is that your code is perfectly valid Gradle code. The task jar is of type Jar. Every task of type Jar may be configured using methods called from and into. The method from adds files to the JAR file and the method into sets the destination directory inside the JAR. So instead of copying, your code configures the underlying task jar. However, this has no negative consequences, as this configuration gets applied inside doLast, which only runs once the JAR file has already been created. Something that already happened cannot be configured.

Execute more than one command in a task without breaking incremental build

We use gradle incremental builds and want to run two commands (ideally from one task). One of the solutions here worked getting the two commands running... however it breaks incremental builds.. It looks something like:
task myTask() {
inputs.files(inputFiles)
project.exec {
workingDir web
commandLine('mycmd')
}
project.exec {
workingDir web
commandLine('mysecond-cmd')
}
}
if running a single command and incremental builds is working, the task looked similar to this, the thing that seems to make the difference is the workingDir:
task myTask(type: Exec) {
workingDir myDir // this seems to trigger/enable continuos compilation
commandLine ('myCmd')
}
the best alternative so far is create 3 tasks, one for each of the cmdline tasks I want to run and a third one to group them, which seems dirty.
The question is: Is there a way to run two or more commands in one task with incremental builds still working?
I'll try to answer the question from the comments: how can I signal from a task that has no output files that the build should watch certain files. Unfortunately, this is hard to answer without knowing the exact use case.
To start with, Gradle requires some form of declared output that it can check to see whether a task has run or whether it needs to run. Consider that the task may have failed during the previous run, but the input files haven't changed since then. Should Gradle run the task?
If you have a task that doesn't have any outputs, that means you need to think about why that task should run or not in any given build execution. What's it doing if it's not creating or modifying files or interacting with another part of the system?
Assuming that incremental build is the right solution — it may not be — Gradle does allow you to handle unusual scenarios via TaskOutputs.upToDateWhen(Action). Here's an example that has a straightforward task (gen) that generates a file that acts as an input for a task (runIt) with no outputs:
task gen {
ext.outputDir = "$buildDir/stuff"
outputs.dir outputDir
doLast {
new File(outputDir, "test.txt").text = "Hurray!\n"
}
}
task runIt {
inputs.dir gen
outputs.upToDateWhen { !gen.didWork }
doLast {
println "Running it"
}
}
This specifies that runIt will only run if the input files have changed or the task gen actually did something. In this example, those two scenarios equate to the same thing. If gen runs, then the input files for runIt have changed.
The key thing to understand is that the condition you provide to upToDateWhen() is evaluated during the execution phase of the build, not the configuration phase.
Hope that helps, and if you need any clarification, let me know.

Gradle - Error Copying Files To Project Root

I’m getting the following error whenever I attempt to use a Copy task to copy a file into the root of a project (the same folder I’m running gradle from):
Failed to create MD5 hash for file content.
I thought this was related to the artifacts I was pulling from Artifactory, but that seems to be unrelated. I was able to get the same results with a minimal script.
Is there something obviously wrong with what I’m doing, or does Gradle intentionally disallow such things?
task fails(type:Copy) {
from 'build/someFile.txt'
into new File('.').absolutePath
}
task works(type:Copy) {
from 'build/someFile.txt'
into new File('.').absolutePath + '/output'
}
Short Answer: Don't copy into the project directory, you are best to use into "$buildDir/someFolder" so that the folder is isolated to this single task, and also so that it will be cleaned by gradle clean
Long Answer: At it's core, Gradle has the concept of an "UP-TO-DATE" check for every single task. If Gradle sees that nothing has changed since last time a task was executed it will use the old result instead of executing again.
UP-TO-DATE checking is implemented by taking a "hash" of the task inputs and task outputs. Since you are using into '.' that means that the entire contents of the project directory is considered a task output (bad)
Gradle uses the .gradle folder for temp files (eg task hashes) It's likely some of these files are locked for writing as Gradle is trying to also read the same files (to calculate the "hash" of the task outputs) causing the error you are seeing
* EDIT *
If you need to copy into the project directory for legacy reasons, you might use Project.copy(...) directly instead of a Copy task. You could manually manage the task inputs/outputs in this case
Eg
task customCopy {
inputs.file "$buildDir/someFile.txt"
outputs.file 'someFile.txt'
doLast {
copy {
from "$buildDir/someFile.txt"
into '.'
}
}
}
Can you believe it, the following works
task myCopy(type: Copy) {
from "$rootDir/app1/src/main/resources/db"
into "$rootDir/app2/src/test/resources/db"
}
test.dependsOn myCopy
and the following doesn't 🤦
task myCopy(type: Copy) {
from '$rootDir/app1/src/main/resources'
into '$rootDir/app2/src/test/resources'
}
test.dependsOn myCopy

Zip task marked as up-to-date

Hi I try to collect plugins from sub-folder, zip them and copy to my export folder.
task buildPlugin {
dependsOn ':empty-plugin:build'
}
task exportPlugin(type: Zip) {
dependsOn buildPlugin
// create new export folder as destination for nightly build
def folder = '/export';
def file = "${project.name}-sdk-${project.version}";
// collect all plugins into cwc-sdk zip file
baseName = file
fileTree("cwc-plugin").each({
if (it.name.endsWith(".zip")) {
from it.absolutePath
}
})
// move cwc-sdk zip file into export destination folder
copy { into folder from file }
delete file
}
I run clean task first. The gradle logs:
:api:compileJava
:api:processResources
:api:classes
:api:jar
:empty-plugin:compileJava
:empty-plugin:processResources
:empty-plugin:classes
:empty-plugin:jar
:empty-plugin:assemble
:empty-plugin:compileTestJava UP-TO-DATE
:empty-plugin:processTestResources UP-TO-DATE
:empty-plugin:testClasses UP-TO-DATE
:empty-plugin:test UP-TO-DATE
:empty-plugin:check UP-TO-DATE
:api:javadoc
:empty-plugin:zip
:empty-plugin:build
:buildPlugin
:exportPlugin UP-TO-DATE
BUILD SUCCESSFUL
Total time: 2.097 secs
While first run :exportPlugin is marked as UP-TO-DATE and I don't get the zipped file from build. When I run :exportPlugin again everything is fine. It's also fine when I chain both tasks manually (rungradle clean, next run gradle buildPlugin, run gradle exportPlugin by doublclick to tasks at IDEA)
I think the order of tasks are still ok. I don't need to work with mustRunAfter.
I also played around with copySpec, buildplugin.outputs.files. But nothing helps.
Can anybody help me to solve this issue for initial build run?
Thanks!
Update:
A Zip task is an abstracted Copy task
AbstractCopyTask is the base class for all copy tasks. (Docu)
I found this comment from Peter Niederwieser
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.
How do I change from it.absolutePath code line inside fileTree loop to be part during configuration phase?

Dynamically created task of type Copy is always UP-TO-DATE

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

Resources