Gradle无法将sysout写入文件 – java.io.IOException:无法删除文件

huangapple go评论73阅读模式
英文:

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

问题

我试图将命令的输出放入某个文件中。在写入文件之前,我想要删除该文件,然后进行写入操作。这是我的Gradle脚本:

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

但是当我运行writefile任务时,我收到以下错误:

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

有任何想法出了什么问题吗?

我猜测的一个问题是Gradle在deletefiles任务开始之前以某种方式锁定了文件。如果是这样,我们应该如何解决这个问题?

编辑 1:
环境信息 -

------------------------------------------------------------
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) 版本 1.10.7,编译于2019年9月1日
JVM:          11.0.4 (Oracle Corporation 11.0.4+11)
OS:           Windows 10 10.0 amd64
英文:

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

答案1

得分: 5

问题是在您的Gradle中,以下行在项目配置阶段执行:

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

这意味着流被创建并且文件被锁定,甚至在任何Gradle任务开始之前就已经发生了。

尝试使用以下Groovy配置来查看问题:

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"
    standardOutput = new MyStream("out.txt")
}

在输出中,您可以看到在任务执行之前就已经创建了流:

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'

要修复它 - 在“Exec”任务执行之前定义standardOutput

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

输出:

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

为了更清晰地说明

例如,这个任务定义:

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

您可以用以下Groovy代码替换在Gradle中:

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

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

writefile.getActions().add( 0, {
    //这些命令将在Gradle决定执行任务`writefile`时稍后执行
    println "write doFirst"
    writefile.standardOutput = new FileOutputStream("out.txt")
} as Action)

writefile.getActions().add( {
    //这些命令将在Gradle决定执行任务`writefile`时稍后执行
    println "write doLast" 
} as Action)

官方文档链接可解释此行为:

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

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

英文:

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

答案2

得分: 1

可能有某个东西在保持文件处于打开状态。有时候,如果IO流没有被正确关闭,就会出现这种情况。Gradle文档表示exec任务会在进程终止后关闭输出流,所以那不应该是问题。

这可能是Windows上已知的删除问题。例如:
https://carlrice.io/blog/gradle-clean-cant-delete-file-on-windows

https://github.com/gradle/gradle/issues/9813 (不过那个问题是关于文件夹的)

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

我会使用--debug选项运行构建,以捕获更多异常的详细信息。

你是在使用Gradle守护进程吗?试试不使用它。因为它是一个持久化的进程,也许出于某种原因,它正在保持对那个文件的文件句柄打开状态。

英文:

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.

huangapple
  • 本文由 发表于 2020年9月27日 00:28:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/64080056.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定