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