Gradle unable to write sysout to file - java.io.IOException: Unable to delete file

Viewed 398

I am trying to put output of a command to some file. Before writing the file i want to delete the file and than do write operation. Here is my gradle script


group 'org.example'
version '1.0-SNAPSHOT'

task deletefiles(type: Delete){
    delete "$projectDir/somefile.txt"
}

task writefile(type: Exec, dependsOn: deletefiles){
    commandLine 'echo', "Hello World"
    standardOutput = new FileOutputStream("$projectDir/somefile.txt")
}

But when i run writefile tasks , I get below error

Execution failed for task ':deletefiles'.
> java.io.IOException: Unable to delete file 'D:\workspace\code\repo\sample-gradle-mono\somefile.txt'

Any idea what's wrong?

One thing i guess is that gradle is somehow acquiring lock on file before deletefiles tasks could start. If that is the case, How can we achieve this?

Edit 1: Enviroment info -

------------------------------------------------------------
Gradle 6.3
------------------------------------------------------------

Build time:   2020-03-24 19:52:07 UTC
Revision:     bacd40b727b0130eeac8855ae3f9fd9a0b207c60

Kotlin:       1.3.70
Groovy:       2.5.10
Ant:          Apache Ant(TM) version 1.10.7 compiled on September 1 2019
JVM:          11.0.4 (Oracle Corporation 11.0.4+11)
OS:           Windows 10 10.0 amd64
2 Answers

the problem that in your gradle the following line executed at project configuration phase

standardOutput = new FileOutputStream("$projectDir/somefile.txt")

it means that stream is created and file locked even before any gradle task was started..

try this groovy configuration to see the issue:


class MyStream extends FileOutputStream{
    MyStream(String f){ 
        super(f)
        println "write $f stream created" 
    }
}

task deletefiles(type: Delete){
    println "delete init"
    doFirst{
        println "delete doFirst" //triggered just before deletion
    }
    delete "out.txt"
}

task writefile(type: Exec, dependsOn: deletefiles){
    println "write init"
    commandLine 'cmd', '/C', 'echo', "Hello World"
    standardOutput = new MyStream("out.txt")
}

in output you can see that stream is created before tasks execution:

cmd> gradle writeFile

> Configure project :
delete init
write init
write out.txt stream created

> Task :deletefiles FAILED
delete doFirst

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':deletefiles'.
> java.io.IOException: Unable to delete file 'out.txt'

To fix it - define the standardOutput just before the "Exec" task is executed:

class MyStream extends FileOutputStream{
    MyStream(String f){ 
        super(f)
        println "write $f stream created" 
    }
}

task deletefiles(type: Delete){
    println "delete init"
    doFirst{
        println "delete doFirst"
    }
    delete "out.txt"
}

task writefile(type: Exec, dependsOn: deletefiles){
    println "write init"
    commandLine 'cmd', '/C', 'echo', "Hello World"
    doFirst{
        println "write doFirst"
        standardOutput = new MyStream("out.txt")
    }
    doLast{
        println "write doLast"
    }
}

output:

cmd>gradle writeFile

> Configure project :
delete init
write init

> Task :deletefiles
delete doFirst

> Task :writefile
write doFirst
write out.txt stream created
write doLast

BUILD SUCCESSFUL in 3s
2 actionable tasks: 2 executed

to add some clarity:

for example the this task definition

task writefile(type: Exec, dependsOn: deletefiles){
    println "write init"
    commandLine 'cmd', '/C', 'echo', "Hello World"
    doFirst{
        println "write doFirst"
        standardOutput = new MyStream("out.txt")
    }
    doLast{
        println "write doLast"
    }
}

you could replace with the following groovy code in your gradle:

def writefile = project.task( [type: Exec, dependsOn: deletefiles], 'writeFile' )

println "write init"
writefile.setCommandLine( ['cmd', '/C', 'echo', "Hello World"] )

writefile.getActions().add( 0, {
    //those commands will be executed later when task `writefile` decided to be executed by gradle
    println "write doFirst"
    writefile.standardOutput = new FileOutputStream("out.txt")
} as Action)

writefile.getActions().add( {
    //those commands will be executed later when task `writefile` decided to be executed by gradle
    println "write doLast" 
} as Action)


links to official documentation that could explain this behavior:

https://docs.gradle.org/current/userguide/build_lifecycle.html#sec:settings_file

https://docs.gradle.org/current/userguide/more_about_tasks.html

It is possible that something is holding the file open. Sometimes if IO streams are not properly closed this can happen. Gradle docs say that exec task closes the output stream after the process terminates, so that shouldn't be the problem. It could be a known issue with deleting on Windows. E.g.: https://carlrice.io/blog/gradle-clean-cant-delete-file-on-windows

https://github.com/gradle/gradle/issues/9813 (but that one is about folders)

https://www.eknori.de/2020-09-18/gradle-execution-failed-for-task-appclean-unable-to-delete-file/

I would run the build with --debug to capture more details of the exception.

Are you running with the Gradle daemon? Try without it. Since it is a persistent process, maybe for some reason it has a file handle open to that file.

Related