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 19f9ffc2b03e6f..3894c73d90e89e 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 @@ private static void setLocations(OptionsParser optionsParser, StandardJavaFileMa 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 @@ public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOExcept 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() { + @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 44e0ed753a69fa..815ac7476dce1a 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 @@ public void diagnosticWithoutSource() throws Exception { 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 outputEntries = readJar(output.toFile()); + assertThat(outputEntries.keySet()) + .containsExactly( + "META-INF/", "META-INF/MANIFEST.MF", "A.class", "B.class", "resource.properties"); + } }