From eb0b52c1a7a7566cc8039d628affc7071783ea57 Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Tue, 11 Aug 2026 05:32:18 -0700 Subject: [PATCH] Close the coverage post-processing `FileOutErr` before deleting its files (https://github.com/bazelbuild/bazel/pull/30662) ### Description `appendCoverageLog` copies `coverage.log` and `coverage.err` into the test log and then deletes them, but the `FileOutErr` that the coverage post-processing spawn wrote them through was never closed. This closes it before the delete, and also closes it on the paths that rethrow, which previously leaked the handles. ### Motivation Deleting a file that still has an open handle is fine on POSIX, but not on Windows. Every affected test then fails with: ``` ERROR: ... Testing //tests/alwayslink_srcs:alwayslink_test failed: java.io.IOException: C:/b/skckkmg2/execroot/_main/bazel-out/x64_windows-fastbuild/testlogs/tests/alwayslink_srcs/alwayslink_test/coverage.err (Permission denied) ``` The bug is older than that, but it only became reachable for Bazel users with 8e68c24713f7d9b83d4eb364a64c8d6df26b261c, which flipped `--experimental_split_coverage_postprocessing` to default true. ### Build API Changes No ### Checklist - [ ] I have added tests for the new use cases (if any). - [ ] I have updated the documentation (if applicable). ### Release Notes Closes #30662. PiperOrigin-RevId: 962724931 Change-Id: Icb52f15e33291676ee7f96a0d2d4b22235bd5536 (cherry picked from commit 53428ca4a01e330c29a01e92487abe52177c583a) --- .../devtools/build/lib/exec/StandaloneTestStrategy.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/google/devtools/build/lib/exec/StandaloneTestStrategy.java b/src/main/java/com/google/devtools/build/lib/exec/StandaloneTestStrategy.java index 46ffcc07daacb7..1be5cc9382f11b 100644 --- a/src/main/java/com/google/devtools/build/lib/exec/StandaloneTestStrategy.java +++ b/src/main/java/com/google/devtools/build/lib/exec/StandaloneTestStrategy.java @@ -786,11 +786,13 @@ private TestAttemptResult runTestAttempt( if (e.isCatastrophic()) { closeSuppressed(e, streamed); closeSuppressed(e, fileOutErr); + closeSuppressed(e, coverageOutErr); throw e; } if (!e.getSpawnResult().setupSuccess()) { closeSuppressed(e, streamed); closeSuppressed(e, fileOutErr); + closeSuppressed(e, coverageOutErr); // Rethrow as the test could not be run and thus there's no point in retrying. throw e; } @@ -801,10 +803,12 @@ private TestAttemptResult runTestAttempt( } catch (ExecException | InterruptedException e) { closeSuppressed(e, streamed); closeSuppressed(e, fileOutErr); + closeSuppressed(e, coverageOutErr); throw e; } // Append all output from the coverage spawn to the test log. + coverageOutErr.close(); writeOutFile(coverageOutErr.getErrorPath(), coverageOutErr.getOutputPath()); appendCoverageLog(coverageOutErr, fileOutErr); }