How to determine why gradle threat directory as stale?


I have task, imported from Ant XML file and then I configure their output:

sencha_init {
    outputs.dir 'ext'
    outputs.files fileTree('ext')

If I also add directory ext as target fro clean:

clean {
    delete 'bootstrap.js', 'bootstrap.css', 'ext'

Gradle immediately begin delete it:

:portal-ui:sencha_init (Thread[Daemon worker,5,main]) started.
Deleting stale output file: /builds/Hubbitus/portal/portal-ui/ext/build/packages/ext-theme-crisp-touch/build/resources/images/grid/dirty.png
Deleting stale output file: /builds/Hubbitus/portal/portal-ui/ext/packages/ext-theme-classic-sandbox/build/resources/images/tree/elbow-line.gif

How I can determine (before start debugging) why gradle threat it as stale and delete despite it marked as output of task (made by example and this)?

The example contains two tasks that output to the same folder, but only one task correctly declares that folder as an output. The stale output cleanup for the second task removes the output of the first task because Gradle has not been configured to treat the folder as an output for the first task. Under these circumstances, the second task should be creating everything in the output folder. Anything already there should be safe to delete.

Marking the directory as an output of a task does not prevent Gradle from deleting it. Gradle will delete the contents of the output directory before any task that declares it writes to it, but not after. Therefore, it is important that all tasks that write to same output directory declare it as an output, but it is more preferable an output directory is not shared between tasks. Most importantly, output directories should not also be source directories.

Sorry, but no.
Clean task does not outputs any. There I declare directories which should be cleared on gradle clean. Is it wrong specification?

The first paragraph is not about your build. It’s about the tasks in your “by example” reference ( That example did contain two tasks that output to the same folder, and caused undesired deletion of the first task’s outputs by the second task. The solution was to make sure both the first and second properly declared the output, which prevents the second task from deleting the contents after the first task has already run.

Your post seemed to indicate that you expect that properly declaring the outputs would prevent Gradle from ever deleting the contents of the output directory. This is not the case. Gradle will delete the contents when necessary for build correctness (even without running clean).

The deletion shouldn’t be unexpected with what you’ve provided. However, if this deletion causes an issue for the build, you will need to provide additional information about the source of these files to help with additional configuration:


My intention is simple - i want delete ext directory on clean task. But why output content deleted if it result of task?


May be I need more describe what I want achieve:

clean {
    delete 'bootstrap.js', 'bootstrap.css', 'ext'

sencha_init {
    outputs.dir 'ext'
    outputs.files fileTree('ext')

sencha_refresh {
    dependsOn sencha_init

    inputs.dir 'app'
    inputs.dir 'ext'
    inputs.file 'app.json'

    packages_dir.listFiles().each() { package_dir -> inputs.dir new File(package_dir, 'src') }
    outputs.file 'bootstrap.js'
    outputs.file 'bootstrap.json'

In that snippet I expect:

  1. Gradle task clean delete ext directory - it work as expected
  2. Task sencha_init place content into ext directory prepare it for sencha_refresh - it works
  3. sencha_refresh continue use ext directory content to build application. But before run it gradle clean ext directory and it failed.

Now, as workaround I just drop ext from clean delete configuration - and all works. Except of easy cleaning.

Could you please:

  1. Give me hint how to fix situation
  2. And how to actually understand gradle logic why directory cleaned?

The purpose of declaring inputs and outputs is to allow Gradle to determine if the task is UP-TO-DATE. If the inputs have not changed and the outputs have not been tampered with, there is no need to do the work again. This works by snapshotting the input files and output files. Gradle only considers a file in the output directory to be an output of the task if is created or modified by the task. This makes sure that if two tasks write a different file into the same directory, deleting one file does not force the other task to run.

When you declare that clean should delete the ext folder, you are informing Gradle that everything contained in ext is safe to delete. You wouldn’t allow clean to delete a folder if it’s not something that can be recreated by the build. Since sencha_init is the only task with ext declared as an output folder, the sencha_init task should be creating absolutely everything that is contained in the ext folder.

If you have any files in ext before Gradle has been able to snapshot it, either due to a change in output folders, a Gradle upgrade, or even deletion on the .gradle folder, Gradle cannot accurately determine whether the file in the output folder would have been created by running the task and should be snapshotted. Therefore, to make sure this build and future builds are accurate, it does not run clean, but it removes the stale outputs of that task before running the task the first time. It should not be a problem to remove the exclusive outputs of sencha_init because the execution of sencha_init should immediately recreate everything that is actually required.

If after stale outputs are removed for sencha_init, the sencha_init task does not create all of the files that you need for sencha_refresh, this suggests that there is something other than sencha_init that is creating some of the required files in the ext folder or impacting how sencha_init works. You would need to know what that is in order to address the issue.

In any case, the lines Deleting stale output file: ... are not the problem. Rather, the problem would be in whatever should be recreating non-stale versions of those files that are removed.

1 Like