I’m implementing a custom task that uses project.exec
in its @TaskAction
.
This task is part of a custom plugin.
Here is a simplified implementation of the @TaskAction
@TaskAction
fun run(){
// OutputStream to where the scanner std and err outputs are written
val consoleLogFile: File = project.layout.buildDirectory.file("logs/console.log").get().asFile
consoleLogFile.parentFile.mkdirs()
val fileOutStream: FileOutputStream = FileOutputStream(consoleLogFile)
// Execute scanner
val result = project.exec {
// Set both std and err outputs to a single file
it.setStandardOutput(fileOutStream)
it.setErrorOutput(fileOutStream)
it.commandLine(composeScannerCommand())
}
}
The composeScannerCommand()
creates an array with executable and parameters. Simple. It does detect check whether it’s running on Win or Unix, which translates either into cmd /c scanner.bat
or just scanner
. But I believe this info isn’t relevant because my tests fail after the tool’s execution…
As for the functional tests, I simply read that logs/console.log
and do a content assertion (omitted).
val runner = GradleRunner.create()
runner.forwardOutput()
runner.withPluginClasspath()
runner.withArguments("runScanner", "--info")
runner.withProjectDir(projectDir)
val result = runner.build()
// Read build console.log file for the mocked scanner, which simply prints the parameters
val console = projectDir.resolve("build/logs/console.log").readText()
As a result, I’m getting the following Exception:
Could not read standard output of command '/tmp/junit9281083532723264258/scanner/bin/scanner'.
java.io.IOException: Stream Closed
at java.base/java.io.FileOutputStream.writeBytes(Native Method)
at java.base/java.io.FileOutputStream.write(FileOutputStream.java:354)
at org.gradle.process.internal.streams.ExecOutputHandleRunner.writeBuffer(ExecOutputHandleRunner.java:97)
at org.gradle.process.internal.streams.ExecOutputHandleRunner.lambda$forwardContent$0(ExecOutputHandleRunner.java:81)
at org.gradle.internal.operations.CurrentBuildOperationRef.with(CurrentBuildOperationRef.java:70)
at org.gradle.process.internal.streams.ExecOutputHandleRunner.forwardContent(ExecOutputHandleRunner.java:80)
at org.gradle.process.internal.streams.ExecOutputHandleRunner.run(ExecOutputHandleRunner.java:64)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:47)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:829)
What’s most interesting is that:
- The Exception happens in Linux only! The same tests work fine in Windows.
- If I run two tests with the test block above (each in its own temporary directory!), one will succeed, the other will throw the Exception. If I run the successful test alone, it then throws the Exception itself. That’s one test is influencing the other, although the
console.log
are different, in separate temp dir.
Info
------------------------------------------------------------
Gradle 8.3
------------------------------------------------------------
Build time: 2023-08-17 07:06:47 UTC
Revision: 8afbf24b469158b714b36e84c6f4d4976c86fcd5
Kotlin: 1.9.0
Groovy: 3.0.17
Ant: Apache Ant(TM) version 1.10.13 compiled on January 4 2023
JVM: 11.0.20 (Ubuntu 11.0.20+8-post-Ubuntu-1ubuntu120.04)
OS: Linux 5.10.16.3-microsoft-standard-WSL2 amd64
Any idea what could be causing the issue?
As an alternative, if I use a ByteArrayOutputStream
instead of FileOutputStream
and then write it into a file after project.exec
(using again a FileOutputStream
), then it works fine. But looks workaroundish.