Create temp directories instead of making Bazel create them for us

PiperOrigin-RevId: 335459680
diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/JavaLibraryBuildRequest.java b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/JavaLibraryBuildRequest.java
index 6dd1255..9cf409c 100644
--- a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/JavaLibraryBuildRequest.java
+++ b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/JavaLibraryBuildRequest.java
@@ -14,13 +14,15 @@
 
 package com.google.devtools.build.buildjar;
 
-import static com.google.common.base.MoreObjects.firstNonNull;
+import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.collect.ImmutableList.toImmutableList;
 import static com.google.common.collect.ImmutableSet.toImmutableSet;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.io.MoreFiles;
 import com.google.devtools.build.buildjar.instrumentation.JacocoInstrumentationProcessor;
 import com.google.devtools.build.buildjar.javac.BlazeJavacArguments;
 import com.google.devtools.build.buildjar.javac.JavacOptions;
@@ -29,6 +31,7 @@
 import com.google.devtools.build.buildjar.javac.plugins.dependency.DependencyModule;
 import com.google.devtools.build.buildjar.javac.plugins.processing.AnnotationProcessingModule;
 import java.io.IOException;
+import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
@@ -133,11 +136,11 @@
       depsBuilder.setTargetLabel(optionsParser.getTargetLabel());
     }
     this.dependencyModule = depsBuilder.build();
+    this.sourceGenDir =
+        deriveDirectory(optionsParser.getTargetLabel(), optionsParser.getOutputJar(), "_sources");
 
     AnnotationProcessingModule.Builder processingBuilder = AnnotationProcessingModule.builder();
-    if (optionsParser.getSourceGenDir() != null) {
-      processingBuilder.setSourceGenDir(Paths.get(optionsParser.getSourceGenDir()));
-    }
+    processingBuilder.setSourceGenDir(sourceGenDir);
     if (optionsParser.getManifestProtoPath() != null) {
       processingBuilder.setManifestProtoPath(Paths.get(optionsParser.getManifestProtoPath()));
     }
@@ -162,10 +165,10 @@
     this.processorPath = asPaths(optionsParser.getProcessorPath());
     this.processorNames = optionsParser.getProcessorNames();
     this.builtinProcessorNames = ImmutableSet.copyOf(optionsParser.getBuiltinProcessorNames());
-    // Since the default behavior of this tool with no arguments is "rm -fr <classDir>", let's not
-    // default to ".", shall we?
-    this.classDir = asPath(firstNonNull(optionsParser.getClassDir(), "classes"));
-    this.tempDir = asPath(firstNonNull(optionsParser.getTempDir(), "_tmp"));
+    this.classDir =
+        deriveDirectory(optionsParser.getTargetLabel(), optionsParser.getOutputJar(), "_classes");
+    this.tempDir =
+        deriveDirectory(optionsParser.getTargetLabel(), optionsParser.getOutputJar(), "_tmp");
     this.outputJar = asPath(optionsParser.getOutputJar());
     this.nativeHeaderOutput = asPath(optionsParser.getNativeHeaderOutput());
     for (Map.Entry<String, List<String>> entry : optionsParser.getPostProcessors().entrySet()) {
@@ -179,13 +182,30 @@
       }
     }
     this.javacOpts = ImmutableList.copyOf(optionsParser.getJavacOpts());
-    this.sourceGenDir = asPath(optionsParser.getSourceGenDir());
     this.generatedSourcesOutputJar = asPath(optionsParser.getGeneratedSourcesOutputJar());
     this.generatedClassOutputJar = asPath(optionsParser.getManifestProtoPath());
     this.targetLabel = optionsParser.getTargetLabel();
     this.injectingRuleKind = optionsParser.getInjectingRuleKind();
   }
 
+  /**
+   * Derive a temporary directory path based on the path to the output jar, to avoid breaking
+   * fragile assumptions made by the implementation of javahotswap.
+   */
+  // TODO(b/169793789): kill this with fire if javahotswap starts using jars instead of classes
+  @VisibleForTesting
+  static Path deriveDirectory(String label, String outputJar, String suffix) throws IOException {
+    if (outputJar == null || label == null) {
+      // TODO(b/169944970): require --output to be set and fix up affected tests
+      return Files.createTempDirectory(suffix);
+    }
+    checkArgument(label.contains(":"), label);
+    Path path = Paths.get(outputJar);
+    String name = MoreFiles.getNameWithoutExtension(path);
+    String base = label.substring(label.lastIndexOf(':') + 1);
+    return path.resolveSibling("_javac").resolve(base).resolve(name + suffix);
+  }
+
   private static ImmutableList<Path> asPaths(Collection<String> paths) {
     return paths.stream().map(Paths::get).collect(toImmutableList());
   }
diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/OptionsParser.java b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/OptionsParser.java
index 802fa6e..a3f2bed 100644
--- a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/OptionsParser.java
+++ b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/OptionsParser.java
@@ -70,7 +70,6 @@
   private int fullClasspathLength = -1;
   private int reducedClasspathLength = -1;
 
-  private String sourceGenDir;
   private String generatedSourcesOutputJar;
   private String manifestProtoPath;
 
@@ -89,9 +88,6 @@
   private String outputJar;
   @Nullable private String nativeHeaderOutput;
 
-  private String classDir;
-  private String tempDir;
-
   private final Map<String, List<String>> postProcessors = new LinkedHashMap<>();
 
   private boolean compressJar;
@@ -166,9 +162,6 @@
         case "--reduced_classpath_length":
           reducedClasspathLength = Integer.parseInt(getArgument(argQueue, arg));
           break;
-        case "--sourcegendir":
-          sourceGenDir = getArgument(argQueue, arg);
-          break;
         case "--generated_sources_output":
           generatedSourcesOutputJar = getArgument(argQueue, arg);
           break;
@@ -215,13 +208,10 @@
           nativeHeaderOutput = getArgument(argQueue, arg);
           break;
         case "--classdir":
-          classDir = getArgument(argQueue, arg);
-          break;
-        case "--tempdir":
-          tempDir = getArgument(argQueue, arg);
-          break;
         case "--gendir":
-          // TODO(bazel-team) - remove when Bazel no longer passes this flag to buildjar.
+        case "--sourcegendir":
+        case "--tempdir":
+          // TODO(bazel-team) - remove when Bazel no longer passes these flags to buildjar.
           getArgument(argQueue, arg);
           break;
         case "--post_processor":
@@ -408,10 +398,6 @@
     return reducedClasspathLength;
   }
 
-  public String getSourceGenDir() {
-    return sourceGenDir;
-  }
-
   public String getGeneratedSourcesOutputJar() {
     return generatedSourcesOutputJar;
   }
@@ -465,14 +451,6 @@
     return nativeHeaderOutput;
   }
 
-  public String getClassDir() {
-    return classDir;
-  }
-
-  public String getTempDir() {
-    return tempDir;
-  }
-
   public Map<String, List<String>> getPostProcessors() {
     return postProcessors;
   }
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 694330b..b2a4da2 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
@@ -14,7 +14,6 @@
 
 package com.google.devtools.build.buildjar;
 
-import static com.google.common.base.MoreObjects.firstNonNull;
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static java.util.Locale.ENGLISH;
 
@@ -158,13 +157,17 @@
     DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<>();
     StringWriter output = new StringWriter();
     JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
-    Path tempDir = Paths.get(firstNonNull(optionsParser.getTempDir(), "_tmp"));
+    Path tempDir = Files.createTempDirectory("_tmp");
     Path nativeHeaderDir = tempDir.resolve("native_headers");
     Files.createDirectories(nativeHeaderDir);
+    Path sourceGenDir = tempDir.resolve("sources");
+    Files.createDirectories(sourceGenDir);
+    Path classDir = tempDir.resolve("classes");
+    Files.createDirectories(classDir);
     boolean ok;
     try (StandardJavaFileManager fileManager =
         javaCompiler.getStandardFileManager(diagnosticCollector, ENGLISH, UTF_8)) {
-      setLocations(optionsParser, fileManager, nativeHeaderDir);
+      setLocations(optionsParser, fileManager, nativeHeaderDir, sourceGenDir, classDir);
       ImmutableList<JavaFileObject> sources = getSources(optionsParser, fileManager);
       if (sources.isEmpty()) {
         ok = true;
@@ -182,10 +185,10 @@
       }
     }
     if (ok) {
-      writeOutput(optionsParser);
+      writeOutput(classDir, optionsParser);
       writeNativeHeaderOutput(optionsParser, nativeHeaderDir);
     }
-    writeGeneratedSourceOutput(optionsParser);
+    writeGeneratedSourceOutput(sourceGenDir, optionsParser);
     // the jdeps output doesn't include any information about dependencies, but Bazel still expects
     // the file to be created
     if (optionsParser.getOutputDepsProtoFile() != null) {
@@ -254,7 +257,11 @@
 
   /** Sets the compilation search paths and output directories. */
   private static void setLocations(
-      OptionsParser optionsParser, StandardJavaFileManager fileManager, Path nativeHeaderDir)
+      OptionsParser optionsParser,
+      StandardJavaFileManager fileManager,
+      Path nativeHeaderDir,
+      Path sourceGenDir,
+      Path classDir)
       throws IOException {
     fileManager.setLocation(StandardLocation.CLASS_PATH, toFiles(optionsParser.getClassPath()));
     Iterable<File> bootClassPath = toFiles(optionsParser.getBootClassPath());
@@ -264,15 +271,11 @@
     }
     fileManager.setLocation(
         StandardLocation.ANNOTATION_PROCESSOR_PATH, toFiles(optionsParser.getProcessorPath()));
-    if (optionsParser.getSourceGenDir() != null) {
-      setOutputLocation(
-          fileManager, StandardLocation.SOURCE_OUTPUT, Paths.get(optionsParser.getSourceGenDir()));
-    }
+    setOutputLocation(fileManager, StandardLocation.SOURCE_OUTPUT, sourceGenDir);
     if (optionsParser.getNativeHeaderOutput() != null) {
       setOutputLocation(fileManager, StandardLocation.NATIVE_HEADER_OUTPUT, nativeHeaderDir);
     }
-    setOutputLocation(
-        fileManager, StandardLocation.CLASS_OUTPUT, Paths.get(optionsParser.getClassDir()));
+    setOutputLocation(fileManager, StandardLocation.CLASS_OUTPUT, classDir);
   }
 
   private static void setOutputLocation(
@@ -300,14 +303,15 @@
   }
 
   /** Writes a jar containing any sources generated by annotation processors. */
-  private static void writeGeneratedSourceOutput(OptionsParser optionsParser) throws IOException {
+  private static void writeGeneratedSourceOutput(Path sourceGenDir, OptionsParser optionsParser)
+      throws IOException {
     if (optionsParser.getGeneratedSourcesOutputJar() == null) {
       return;
     }
     JarCreator jar = new JarCreator(optionsParser.getGeneratedSourcesOutputJar());
     jar.setNormalize(true);
     jar.setCompression(optionsParser.compressJar());
-    jar.addDirectory(optionsParser.getSourceGenDir());
+    jar.addDirectory(sourceGenDir);
     jar.execute();
   }
 
@@ -327,11 +331,11 @@
   }
 
   /** Writes the class output jar, including any resource entries. */
-  private static void writeOutput(OptionsParser optionsParser) throws IOException {
+  private static void writeOutput(Path classDir, OptionsParser optionsParser) throws IOException {
     JarCreator jar = new JarCreator(optionsParser.getOutputJar());
     jar.setNormalize(true);
     jar.setCompression(optionsParser.compressJar());
-    jar.addDirectory(optionsParser.getClassDir());
+    jar.addDirectory(classDir);
     jar.execute();
   }
 
diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/genclass/GenClass.java b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/genclass/GenClass.java
index 16d4f10..7a38b43 100644
--- a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/genclass/GenClass.java
+++ b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/genclass/GenClass.java
@@ -67,10 +67,11 @@
   public static void main(String[] args) throws IOException {
     GenClassOptions options = GenClassOptionsParser.parse(Arrays.asList(args));
     Manifest manifest = readManifest(options.manifest());
-    deleteTree(options.tempDir());
-    Files.createDirectories(options.tempDir());
-    extractGeneratedClasses(options.classJar(), manifest, options.tempDir());
-    writeOutputJar(options);
+    Path tempDir = Files.createTempDirectory("tmp");
+    Files.createDirectories(tempDir);
+    extractGeneratedClasses(options.classJar(), manifest, tempDir);
+    writeOutputJar(tempDir, options);
+    deleteTree(tempDir);
   }
 
   /** Reads the compilation manifest. */
@@ -161,11 +162,11 @@
   }
 
   /** Writes the generated class files to the output jar. */
-  private static void writeOutputJar(GenClassOptions options) throws IOException {
+  private static void writeOutputJar(Path tempDir, GenClassOptions options) throws IOException {
     JarCreator output = new JarCreator(options.outputJar().toString());
     output.setCompression(true);
     output.setNormalize(true);
-    output.addDirectory(options.tempDir().toString());
+    output.addDirectory(tempDir);
     output.execute();
   }
 }
diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/genclass/GenClassOptions.java b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/genclass/GenClassOptions.java
index 9e46d96..708d010 100644
--- a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/genclass/GenClassOptions.java
+++ b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/genclass/GenClassOptions.java
@@ -18,7 +18,7 @@
 
 import java.nio.file.Path;
 
-/** The options for a {@GenClass} action. */
+/** The options for a {@link GenClass} action. */
 public final class GenClassOptions {
 
   /** A builder for {@link GenClassOptions}. */
@@ -26,7 +26,6 @@
     private Path manifest;
     private Path classJar;
     private Path outputJar;
-    private Path tempDir;
 
     public Builder() {}
 
@@ -42,25 +41,19 @@
       this.outputJar = outputJar;
     }
 
-    public void setTempDir(Path tempDir) {
-      this.tempDir = tempDir;
-    }
-
     GenClassOptions build() {
-      return new GenClassOptions(manifest, classJar, outputJar, tempDir);
+      return new GenClassOptions(manifest, classJar, outputJar);
     }
   }
 
   private final Path manifest;
   private final Path classJar;
   private final Path outputJar;
-  private final Path tempDir;
 
-  private GenClassOptions(Path manifest, Path classJar, Path outputJar, Path tempDir) {
+  private GenClassOptions(Path manifest, Path classJar, Path outputJar) {
     this.manifest = checkNotNull(manifest);
     this.classJar = checkNotNull(classJar);
     this.outputJar = checkNotNull(outputJar);
-    this.tempDir = checkNotNull(tempDir);
   }
 
   /** The path to the compilation manifest proto. */
@@ -78,11 +71,6 @@
     return outputJar;
   }
 
-  /** The path to the temp directory. */
-  public Path tempDir() {
-    return tempDir;
-  }
-
   /** Returns a builder for {@link GenClassOptions}. */
   public static Builder builder() {
     return new Builder();
diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/genclass/GenClassOptionsParser.java b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/genclass/GenClassOptionsParser.java
index 606185b..795eac0 100644
--- a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/genclass/GenClassOptionsParser.java
+++ b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/genclass/GenClassOptionsParser.java
@@ -37,7 +37,8 @@
           builder.setOutputJar(readPath(it));
           break;
         case "--temp_dir":
-          builder.setTempDir(readPath(it));
+          // TODO(b/169793789): remove once Blaze no longer passes the flag
+          readPath(it);
           break;
         default:
           throw new IllegalArgumentException(
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 bfcc547..36fdccd 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
@@ -14,6 +14,7 @@
 
 package com.google.devtools.build.buildjar;
 
+import static com.google.common.base.StandardSystemProperty.JAVA_HOME;
 import static com.google.common.truth.Truth.assertThat;
 import static java.nio.charset.StandardCharsets.UTF_8;
 
@@ -91,11 +92,7 @@
                 "--output",
                 output.toString(),
                 "--bootclasspath",
-                Paths.get(System.getProperty("java.home")).resolve("lib/rt.jar").toString(),
-                "--tempdir",
-                temporaryFolder.newFolder().toString(),
-                "--classdir",
-                temporaryFolder.newFolder().toString()));
+                Paths.get(JAVA_HOME.value()).resolve("lib/rt.jar").toString()));
 
     assertThat(result.output()).isEmpty();
     assertThat(result.ok()).isTrue();
@@ -136,11 +133,7 @@
                 "--output",
                 output.toString(),
                 "--bootclasspath",
-                Paths.get(System.getProperty("java.home")).resolve("lib/rt.jar").toString(),
-                "--tempdir",
-                temporaryFolder.newFolder().toString(),
-                "--classdir",
-                temporaryFolder.newFolder().toString()));
+                Paths.get(System.getProperty("java.home")).resolve("lib/rt.jar").toString()));
 
     assertThat(result.output()).contains("possible fall-through");
     assertThat(result.ok()).isFalse();
@@ -175,11 +168,7 @@
                 "--sources",
                 source.toString(),
                 "--output",
-                output.toString(),
-                "--tempdir",
-                temporaryFolder.newFolder().toString(),
-                "--classdir",
-                temporaryFolder.newFolder().toString()));
+                output.toString()));
 
     assertThat(result.output()).contains("note: Some messages have been simplified");
     assertThat(result.ok()).isFalse();
@@ -221,11 +210,7 @@
                 "--output",
                 output.toString(),
                 "--bootclasspath",
-                Paths.get(System.getProperty("java.home")).resolve("lib/rt.jar").toString(),
-                "--tempdir",
-                temporaryFolder.newFolder().toString(),
-                "--classdir",
-                classDir.toString()));
+                Paths.get(System.getProperty("java.home")).resolve("lib/rt.jar").toString()));
 
     assertThat(result.output()).isEmpty();
     assertThat(result.ok()).isTrue();
@@ -267,11 +252,7 @@
                 "--output",
                 output.toString(),
                 "--bootclasspath",
-                Paths.get(System.getProperty("java.home")).resolve("lib/rt.jar").toString(),
-                "--tempdir",
-                temporaryFolder.newFolder().toString(),
-                "--classdir",
-                temporaryFolder.newFolder().toString()));
+                Paths.get(System.getProperty("java.home")).resolve("lib/rt.jar").toString()));
 
     assertThat(result.output()).isEmpty();
     assertThat(result.ok()).isTrue();
@@ -314,11 +295,7 @@
                 "--native_header_output",
                 nativeHeaderOutput.toString(),
                 "--bootclasspath",
-                Paths.get(System.getProperty("java.home")).resolve("lib/rt.jar").toString(),
-                "--tempdir",
-                temporaryFolder.newFolder().toString(),
-                "--classdir",
-                temporaryFolder.newFolder().toString()));
+                Paths.get(System.getProperty("java.home")).resolve("lib/rt.jar").toString()));
 
     assertThat(result.output()).isEmpty();
     assertThat(result.ok()).isTrue();