Update Smali, Dx, and DexMerger tasks to use Gradle workers
This allows these tasks to run in parallel rather than in serial, leading to improved build times.
Before:
BUILD SUCCESSFUL in 1m 3s
317 actionable tasks: 192 executed, 125 up-to-date
After:
BUILD SUCCESSFUL in 25s
317 actionable tasks: 192 executed, 125 up-to-date
Change-Id: I9089a9b80cb1f0a570fc3135afbbbc6311fd4a2c
diff --git a/buildSrc/src/main/java/dx/DxTask.java b/buildSrc/src/main/java/dx/DxTask.java
new file mode 100644
index 0000000..d8975dc
--- /dev/null
+++ b/buildSrc/src/main/java/dx/DxTask.java
@@ -0,0 +1,125 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package dx;
+
+import com.google.common.base.Optional;
+import java.io.File;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import javax.inject.Inject;
+import org.gradle.api.DefaultTask;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.InputFile;
+import org.gradle.api.tasks.InputFiles;
+import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.workers.IsolationMode;
+import org.gradle.workers.WorkerExecutor;
+import utils.Utils;
+
+public class DxTask extends DefaultTask {
+
+ private final WorkerExecutor workerExecutor;
+
+ private FileCollection source;
+ private File destination;
+ private Optional<File> dxExecutable = Optional.absent(); // Worker API cannot handle null.
+ private boolean debug;
+
+ @Inject
+ public DxTask(WorkerExecutor workerExecutor) {
+ this.workerExecutor = workerExecutor;
+ }
+
+ @InputFiles
+ public FileCollection getSource() {
+ return source;
+ }
+
+ public void setSource(FileCollection source) {
+ this.source = source;
+ }
+
+ @OutputDirectory
+ public File getDestination() {
+ return destination;
+ }
+
+ public void setDestination(File destination) {
+ this.destination = destination;
+ }
+
+ @InputFile
+ @org.gradle.api.tasks.Optional
+ public File getDxExecutable() {
+ return dxExecutable.orNull();
+ }
+
+ public void setDxExecutable(File dxExecutable) {
+ this.dxExecutable = Optional.fromNullable(dxExecutable);
+ }
+
+ @Input
+ public boolean isDebug() {
+ return debug;
+ }
+
+ public void setDebug(boolean debug) {
+ this.debug = debug;
+ }
+
+ @TaskAction
+ void exec() {
+ workerExecutor.submit(RunDx.class, config -> {
+ config.setIsolationMode(IsolationMode.NONE);
+ config.params(source.getFiles(), destination, dxExecutable, debug);
+ });
+ }
+
+ public static class RunDx implements Runnable {
+ private final Set<File> sources;
+ private final File destination;
+ private final Optional<File> dxExecutable;
+ private final boolean debug;
+
+ @Inject
+ public RunDx(Set<File> sources, File destination, Optional<File> dxExecutable, boolean debug) {
+ this.sources = sources;
+ this.destination = destination;
+ this.dxExecutable = dxExecutable;
+ this.debug = debug;
+ }
+
+ @Override
+ public void run() {
+ try {
+ List<String> command = new ArrayList<>();
+ command.add(dxExecutable.or(Utils::dxExecutable).getCanonicalPath());
+ command.add("--dex");
+ command.add("--output");
+ command.add(destination.getCanonicalPath());
+ if (debug) {
+ command.add("--debug");
+ }
+ for (File source : sources) {
+ command.add(source.getCanonicalPath());
+ }
+
+ Process dx = new ProcessBuilder(command).inheritIO().start();
+ int exitCode = dx.waitFor();
+ if (exitCode != 0) {
+ throw new RuntimeException("dx failed with code " + exitCode);
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+}