Repository: bazelbuild/bazel
Issue: C++ SingleJar on Windows can't deal with empty files
Labels: type: bug
P2
platform: windows
area-Windows
team-OSS

> ATTENTION! Please read and follow:
> - if this is a _question_ about how to build / test / query / deploy using Bazel, ask it on StackOverflow instead: https://stackoverflow.com/questions/tagged/bazel
> - if this is a _discussion starter_, send it to bazel-discuss@googlegroups.com
> - if this is a _bug_ or _feature request_, fill the form below as best as you can.

### Description of the problem / feature request:

It seems like C++ SingleJar on Windows crashes when it encounters an empty file as an input:

```
WARNING: C:\users\philwo\_bazel_philwo\i3dsexk6\execroot\io_bazel\src/tools/singlejar/mapped_file_windows.inc:66: Windows MappedFile cannot handle empty file (bazel-out/x64_windows-fastbuild/genfiles/src/main/java/com/google/devtools/build/lib/runtime/commands/LICENSE)
ERROR: src/tools/singlejar/output_jar.cc:922: bazel-out/x64_windows-fastbuild/genfiles/src/main/java/com/google/devtools/build/lib/runtime/commands/LICENSE
Target //src:bazel.exe failed to build
```

You can easily repro this by changing this line:
https://source.bazel.build/bazel/+/cb9b2afbba3f8d3a1db8bf68e65d06f1b36902f5:src/main/java/com/google/devtools/build/lib/BUILD;l=1273

to just

```
cmd = "touch \"$@\"",
```

and then build Bazel as usual:

```
bazel build //src:bazel.exe
```

### What operating system are you running Bazel on?

Windows 10

### What's the output of `bazel info release`?

I can repro this at Bazel HEAD


Issue comments:
Thanks for the report!

The error comes from here:
https://github.com/bazelbuild/bazel/blob/7fc967c4d6435de2bb4e34aac00ca2e499f55fca/src/tools/singlejar/mapped_file_windows.inc#L66

The caller is here:
https://github.com/bazelbuild/bazel/blob/7fc967c4d6435de2bb4e34aac00ca2e499f55fca/src/tools/singlejar/output_jar.cc#L909

The comment above the failing line in `mapped_file_windows.inc` explains that empty files are not supported. I think the caller should handle empty files instead of attempting to map them.

I'll try to fix this.

Pull request review comments:

Patch that resolved the issue:
diff --git a/src/tools/singlejar/mapped_file_windows.inc b/src/tools/singlejar/mapped_file_windows.inc
index 43e524ae12302a..a35702874baffa 100644
--- a/src/tools/singlejar/mapped_file_windows.inc
+++ b/src/tools/singlejar/mapped_file_windows.inc
@@ -50,8 +50,7 @@ bool MappedFile::Open(const std::string& path) {
   size_t fileSize = temp.QuadPart;
 
   if (fileSize == 0) {
-    // This is where Windows implementation differs from POSIX's.
-    // CreateFileMapping cannot map empty file:
+    // Handle empty files specially, because CreateFileMapping cannot map them:
     //
     // From
     // https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-createfilemappinga
@@ -59,14 +58,10 @@ bool MappedFile::Open(const std::string& path) {
     // An attempt to map a file with a length of 0 (zero) fails with an error
     // code of ERROR_FILE_INVALID. Applications should test for files with a
     // length of 0 (zero) and reject those files.
-    //
-    // Since the test at //src/tools/singlerjar/input_jar_bad_jar_test expects
-    // empty file to fail anyway, returning error here shouldn't be a problem.
-    diag_warn("%s:%d: Windows MappedFile cannot handle empty file (%s)",
-              __FILE__, __LINE__, path.c_str());
-    _close(fd_);
-    fd_ = -1;
-    return false;
+    mapped_start_ = nullptr;
+    mapped_end_ = nullptr;
+    hMapFile_ = INVALID_HANDLE_VALUE;
+    return true;
   }
 
   hMapFile_ = ::CreateFileMapping(
@@ -107,13 +102,17 @@ bool MappedFile::Open(const std::string& path) {
 
 void MappedFile::Close() {
   if (is_open()) {
-    ::UnmapViewOfFile(mapped_start_);
-    ::CloseHandle(hMapFile_);
+    if (mapped_start_) {
+      ::UnmapViewOfFile(mapped_start_);
+    }
+    if (hMapFile_ != INVALID_HANDLE_VALUE) {
+      ::CloseHandle(hMapFile_);
+      hMapFile_ = INVALID_HANDLE_VALUE;
+    }
     _close(fd_);
-    hMapFile_ = INVALID_HANDLE_VALUE;
     fd_ = -1;
     mapped_start_ = mapped_end_ = nullptr;
   }
 }
 
-#endif  // BAZEL_SRC_TOOLS_SINGLEJAR_MAPPED_FILE_WINDOWS_H_
\ No newline at end of file
+#endif  // BAZEL_SRC_TOOLS_SINGLEJAR_MAPPED_FILE_WINDOWS_H_
diff --git a/src/tools/singlejar/output_jar_shell_test.sh b/src/tools/singlejar/output_jar_shell_test.sh
index 961dc10181cd00..9ce549774e72ad 100755
--- a/src/tools/singlejar/output_jar_shell_test.sh
+++ b/src/tools/singlejar/output_jar_shell_test.sh
@@ -66,6 +66,14 @@ function test_new_entries() {
     { echo "build-data.properties is not readable" >&2; exit 1; }
 }
 
+# Regression test https://github.com/bazelbuild/bazel/issues/6820
+function test_empty_resource_file() {
+  local -r out_jar="${TEST_TMPDIR}/out.jar"
+  local -r empty="${TEST_TMPDIR}/empty"
+  echo -n > "$empty"
+  "$singlejar" --output "$out_jar" --resources $empty
+}
+
 run_suite "Misc shell tests"
 #!/bin/bash
 


Test patch: