Clean VanillaJavaBuilder output directories

to ensure outputs from any previous local builds are discarded.

To cherry-pick for #2692.

Fixes #2941

PiperOrigin-RevId: 155089391
diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/VanillaJavaBuilder.java b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/VanillaJavaBuilder.java
index 19f9ffc..3894c73 100644
--- a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/VanillaJavaBuilder.java
+++ b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/VanillaJavaBuilder.java
@@ -249,12 +249,12 @@
         StandardLocation.ANNOTATION_PROCESSOR_PATH, toFiles(optionsParser.getProcessorPath()));
     if (optionsParser.getSourceGenDir() != null) {
       Path sourceGenDir = Paths.get(optionsParser.getSourceGenDir());
-      Files.createDirectories(sourceGenDir);
+      createOutputDirectory(sourceGenDir);
       fileManager.setLocation(
           StandardLocation.SOURCE_OUTPUT, ImmutableList.of(sourceGenDir.toFile()));
     }
     Path classDir = Paths.get(optionsParser.getClassDir());
-    Files.createDirectories(classDir);
+    createOutputDirectory(classDir);
     fileManager.setLocation(StandardLocation.CLASS_OUTPUT, ImmutableList.of(classDir.toFile()));
   }
 
@@ -342,4 +342,32 @@
       return new String(Files.readAllBytes(path), UTF_8);
     }
   }
+
+  private static void createOutputDirectory(Path dir) throws IOException {
+    if (Files.exists(dir)) {
+      try {
+        // TODO(b/27069912): handle symlinks
+        Files.walkFileTree(
+            dir,
+            new SimpleFileVisitor<Path>() {
+              @Override
+              public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
+                  throws IOException {
+                Files.delete(file);
+                return FileVisitResult.CONTINUE;
+              }
+
+              @Override
+              public FileVisitResult postVisitDirectory(Path dir, IOException exc)
+                  throws IOException {
+                Files.delete(dir);
+                return FileVisitResult.CONTINUE;
+              }
+            });
+      } catch (IOException e) {
+        throw new IOException("Cannot clean output directory '" + dir + "'", e);
+      }
+    }
+    Files.createDirectories(dir);
+  }
 }
diff --git a/src/java_tools/buildjar/javatests/com/google/devtools/build/buildjar/VanillaJavaBuilderTest.java b/src/java_tools/buildjar/javatests/com/google/devtools/build/buildjar/VanillaJavaBuilderTest.java
index 44e0ed7..815ac74 100644
--- a/src/java_tools/buildjar/javatests/com/google/devtools/build/buildjar/VanillaJavaBuilderTest.java
+++ b/src/java_tools/buildjar/javatests/com/google/devtools/build/buildjar/VanillaJavaBuilderTest.java
@@ -183,4 +183,55 @@
     assertThat(result.ok()).isFalse();
     assertThat(Files.exists(output)).isFalse();
   }
+
+  @Test
+  public void cleanOutputDirectories() throws Exception {
+    Path source = temporaryFolder.newFile("Test.java").toPath();
+    Path output = temporaryFolder.newFile("out.jar").toPath();
+    Files.write(
+        source,
+        ImmutableList.of(
+            "class A {", //
+            "}"),
+        UTF_8);
+    Path sourceJar = temporaryFolder.newFile("src.srcjar").toPath();
+    try (OutputStream os = Files.newOutputStream(sourceJar);
+        JarOutputStream jos = new JarOutputStream(os)) {
+      jos.putNextEntry(new JarEntry("B.java"));
+      jos.write("class B {}".getBytes(UTF_8));
+    }
+    Path resource = temporaryFolder.newFile("resource.properties").toPath();
+    Files.write(resource, "hello".getBytes(UTF_8));
+
+    Path classDir = temporaryFolder.newFolder().toPath();
+    Files.write(
+        classDir.resolve("extra.class"),
+        new byte[] {(byte) 0xca, (byte) 0xfe, (byte) 0xba, (byte) 0xbe});
+
+    VanillaJavaBuilderResult result =
+        run(
+            ImmutableList.of(
+                "--javacopts",
+                "-Xep:FallThrough:ERROR",
+                "--sources",
+                source.toString(),
+                "--source_jars",
+                sourceJar.toString(),
+                "--output",
+                output.toString(),
+                "--classpath_resources",
+                resource.toString(),
+                "--bootclasspath",
+                Paths.get(System.getProperty("java.home")).resolve("lib/rt.jar").toString(),
+                "--classdir",
+                classDir.toString()));
+
+    assertThat(result.output()).isEmpty();
+    assertThat(result.ok()).isTrue();
+
+    ImmutableMap<String, byte[]> outputEntries = readJar(output.toFile());
+    assertThat(outputEntries.keySet())
+        .containsExactly(
+            "META-INF/", "META-INF/MANIFEST.MF", "A.class", "B.class", "resource.properties");
+  }
 }