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

Kri*_*nom 3 java groovy gradle

我正在尝试将命令的输出放入某个文件。在写入文件之前,我想删除文件而不是进行写入操作。这是我的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")
}
Run Code Online (Sandbox Code Playgroud)

但是当我运行 writefile 任务时,出现以下错误

Execution failed for task ':deletefiles'.
> java.io.IOException: Unable to delete file 'D:\workspace\code\repo\sample-gradle-mono\somefile.txt'
Run Code Online (Sandbox Code Playgroud)

知道出了什么问题吗?

我猜的一件事是 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) 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
Run Code Online (Sandbox Code Playgroud)

dag*_*ett 5

在项目配置阶段在您的 gradle 中执行以下行的问题

standardOutput = new FileOutputStream("$projectDir/somefile.txt")
Run Code Online (Sandbox Code Playgroud)

这意味着即使在启动任何 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" //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")
}
Run Code Online (Sandbox Code Playgroud)

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

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'
Run Code Online (Sandbox Code Playgroud)

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

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"
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

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
Run Code Online (Sandbox Code Playgroud)

增加一些清晰度

例如这个任务定义

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"
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以在您的 gradle 中替换为以下 groovy 代码:

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)

Run Code Online (Sandbox Code Playgroud)

可以解释这种行为的官方文档链接:

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

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