Support workers in CompatDexBuilder

This also fixes the `--use_workers_with_dexbuilder` flag which would pass the worker execution requirements to `CompatDexBuilder` without it knowing how to handle the work requests.

Using a worker here should help with some OOMs issues, and drastically improves build performance. We saw a 29% improvement in clean build times and a 14% improvement in incremental build times where the ABI was invalidated.

Closes #14623.

PiperOrigin-RevId: 443508457
diff --git a/src/tools/android/java/com/google/devtools/build/android/BUILD b/src/tools/android/java/com/google/devtools/build/android/BUILD
index 4774619..592451e 100644
--- a/src/tools/android/java/com/google/devtools/build/android/BUILD
+++ b/src/tools/android/java/com/google/devtools/build/android/BUILD
@@ -25,6 +25,7 @@
     create_executable = 0,
     runtime_deps = [
         ":android_builder_lib",
+        "//src/main/java/com/google/devtools/build/lib/worker:work_request_handlers",
         "//src/tools/android/java/com/google/devtools/build/android/desugar",
         "//src/tools/android/java/com/google/devtools/build/android/desugar/scan",
         "//src/tools/android/java/com/google/devtools/build/android/dexer:dexerdeps",
diff --git a/src/tools/android/java/com/google/devtools/build/android/r8/BUILD b/src/tools/android/java/com/google/devtools/build/android/r8/BUILD
index 39343dd..400d259 100644
--- a/src/tools/android/java/com/google/devtools/build/android/r8/BUILD
+++ b/src/tools/android/java/com/google/devtools/build/android/r8/BUILD
@@ -41,6 +41,7 @@
     }),
     visibility = ["//src/test/java/com/google/devtools/build/android/r8:__pkg__"],
     deps = [
+        "//src/main/java/com/google/devtools/build/lib/worker:work_request_handlers",
         "//src/main/java/com/google/devtools/common/options",
         "//src/tools/android/java/com/google/devtools/build/android:android_builder_lib",
         "//third_party:asm",
diff --git a/src/tools/android/java/com/google/devtools/build/android/r8/CompatDexBuilder.java b/src/tools/android/java/com/google/devtools/build/android/r8/CompatDexBuilder.java
index 6aecd25..ef9ea90 100644
--- a/src/tools/android/java/com/google/devtools/build/android/r8/CompatDexBuilder.java
+++ b/src/tools/android/java/com/google/devtools/build/android/r8/CompatDexBuilder.java
@@ -26,14 +26,22 @@
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.origin.ArchiveEntryOrigin;
 import com.android.tools.r8.origin.PathOrigin;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.io.ByteStreams;
+import com.google.devtools.build.lib.worker.ProtoWorkerMessageProcessor;
+import com.google.devtools.build.lib.worker.WorkRequestHandler;
 import com.google.devtools.common.options.OptionsParsingException;
 import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.PrintStream;
+import java.io.PrintWriter;
 import java.nio.file.Files;
 import java.nio.file.Paths;
+import java.time.Duration;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Enumeration;
 import java.util.List;
 import java.util.Set;
@@ -77,11 +85,60 @@
   public static void main(String[] args)
       throws IOException, InterruptedException, ExecutionException, OptionsParsingException {
     CompatDexBuilder compatDexBuilder = new CompatDexBuilder();
-    compatDexBuilder.processRequest(args);
+    if (ImmutableSet.copyOf(args).contains("--persistent_worker")) {
+      ByteArrayOutputStream buf = new ByteArrayOutputStream();
+      PrintStream ps = new PrintStream(buf, true);
+      PrintStream realStdOut = System.out;
+      PrintStream realStdErr = System.err;
+
+      // Redirect all stdout and stderr output for logging.
+      System.setOut(ps);
+      System.setErr(ps);
+      try {
+        WorkRequestHandler workerHandler =
+            new WorkRequestHandler.WorkRequestHandlerBuilder(
+                    new WorkRequestHandler.WorkRequestCallback(
+                        (request, pw) ->
+                            compatDexBuilder.processRequest(request.getArgumentsList(), pw, buf)),
+                    realStdErr,
+                    new ProtoWorkerMessageProcessor(System.in, realStdOut))
+                .setCpuUsageBeforeGc(Duration.ofSeconds(10))
+                .build();
+        workerHandler.processRequests();
+      } catch (IOException e) {
+        realStdErr.println(e.getMessage());
+        System.exit(1);
+      } finally {
+        System.setOut(realStdOut);
+        System.setErr(realStdErr);
+      }
+    } else {
+      compatDexBuilder.dexEntries(Arrays.asList(args));
+    }
+  }
+
+  private int processRequest(List<String> args, PrintWriter pw, ByteArrayOutputStream buf) {
+    try {
+      dexEntries(args);
+      return 0;
+    } catch (OptionsParsingException e) {
+      pw.println("CompatDexBuilder raised OptionsParsingException: " + e.getMessage());
+      return 1;
+    } catch (IOException | InterruptedException | ExecutionException e) {
+      e.printStackTrace();
+      return 1;
+    } finally {
+      // Write the captured buffer to the work response
+      synchronized (buf) {
+        String captured = buf.toString(UTF_8).trim();
+        buf.reset();
+        pw.print(captured);
+      }
+    }
   }
 
   @SuppressWarnings("JdkObsolete")
-  private void processRequest(String[] args)
+  private void dexEntries(List<String> args)
       throws IOException, InterruptedException, ExecutionException, OptionsParsingException {
     List<String> flags = new ArrayList<>();
     String input = null;