Implement the reduced classpath optimization in turbine
MOE_MIGRATED_REVID=138561608
diff --git a/java/com/google/turbine/deps/Dependencies.java b/java/com/google/turbine/deps/Dependencies.java
index b6aeb6a..110d420 100644
--- a/java/com/google/turbine/deps/Dependencies.java
+++ b/java/com/google/turbine/deps/Dependencies.java
@@ -17,6 +17,10 @@
package com.google.turbine.deps;
import com.google.common.base.Optional;
+import com.google.common.base.Predicates;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.turbine.binder.Binder.BindingResult;
import com.google.turbine.binder.bound.TypeBoundClass;
@@ -27,6 +31,14 @@
import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.lower.Lower.Lowered;
import com.google.turbine.proto.DepsProto;
+import java.io.BufferedInputStream;
+import java.io.IOError;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Collection;
+import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
@@ -95,4 +107,44 @@
addSuperTypes(closure, env, i);
}
}
+
+ /**
+ * Filters a transitive classpath to contain only the entries for direct dependencies, and the
+ * types needed to compile those direct deps as reported by jdeps.
+ *
+ * <p>If no direct dependency information is available the full transitive classpath is returned.
+ */
+ public static Collection<String> reduceClasspath(
+ ImmutableList<String> transitiveClasspath,
+ ImmutableMap<String, String> directJarsToTargets,
+ ImmutableList<String> depsArtifacts) {
+ if (directJarsToTargets.isEmpty()) {
+ // the compilation doesn't support strict deps (e.g. proto libraries)
+ return transitiveClasspath;
+ }
+ Set<String> reduced = new HashSet<>(directJarsToTargets.keySet());
+ for (String path : depsArtifacts) {
+ DepsProto.Dependencies.Builder deps = DepsProto.Dependencies.newBuilder();
+ try (InputStream is = new BufferedInputStream(Files.newInputStream(Paths.get(path)))) {
+ deps.mergeFrom(is);
+ } catch (IOException e) {
+ throw new IOError(e);
+ }
+ for (DepsProto.Dependency dep : deps.build().getDependencyList()) {
+ switch (dep.getKind()) {
+ case EXPLICIT:
+ case IMPLICIT:
+ reduced.add(dep.getPath());
+ break;
+ case INCOMPLETE:
+ case UNUSED:
+ break;
+ default:
+ throw new AssertionError(dep.getKind());
+ }
+ }
+ }
+ // preserve the order of entries in the transitive classpath
+ return Collections2.filter(transitiveClasspath, Predicates.in(reduced));
+ }
}
diff --git a/java/com/google/turbine/main/Main.java b/java/com/google/turbine/main/Main.java
index c6d9bd1..1ba8479 100644
--- a/java/com/google/turbine/main/Main.java
+++ b/java/com/google/turbine/main/Main.java
@@ -41,6 +41,7 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -70,11 +71,14 @@
ImmutableList<CompUnit> units = parseAll(options);
- // TODO(cushon): reduce classpath
+ Collection<String> reducedClasspath =
+ Dependencies.reduceClasspath(
+ options.classPath(), options.directJarsToTargets(), options.depsArtifacts());
+
BindingResult bound =
Binder.bind(
units,
- Iterables.transform(options.classPath(), TO_PATH),
+ Iterables.transform(reducedClasspath, TO_PATH),
Iterables.transform(options.bootClassPath(), TO_PATH));
// TODO(cushon): parallelize
diff --git a/javatests/com/google/turbine/deps/DependenciesTest.java b/javatests/com/google/turbine/deps/DependenciesTest.java
index 71579f5..91a8440 100644
--- a/javatests/com/google/turbine/deps/DependenciesTest.java
+++ b/javatests/com/google/turbine/deps/DependenciesTest.java
@@ -33,7 +33,9 @@
import com.google.turbine.parse.Parser;
import com.google.turbine.proto.DepsProto;
import com.google.turbine.tree.Tree.CompUnit;
+import java.io.BufferedOutputStream;
import java.io.IOException;
+import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -249,4 +251,61 @@
liba, DepsProto.Dependency.Kind.EXPLICIT));
}
}
+
+ @Test
+ public void unreducedClasspathTest() throws IOException {
+ ImmutableList<String> classpath =
+ ImmutableList.of(
+ "a.jar", "b.jar", "c.jar", "d.jar", "e.jar", "f.jar", "g.jar", "h.jar", "i.jar",
+ "j.jar");
+ ImmutableMap<String, String> directJarsToTargets = ImmutableMap.of();
+ ImmutableList<String> depsArtifacts = ImmutableList.of();
+ assertThat(Dependencies.reduceClasspath(classpath, directJarsToTargets, depsArtifacts))
+ .isEqualTo(classpath);
+ }
+
+ @Test
+ public void reducedClasspathTest() throws IOException {
+ Path cdeps = temporaryFolder.newFile("c.jdeps").toPath();
+ Path ddeps = temporaryFolder.newFile("d.jdeps").toPath();
+ Path gdeps = temporaryFolder.newFile("g.jdeps").toPath();
+ ImmutableList<String> classpath =
+ ImmutableList.of(
+ "a.jar", "b.jar", "c.jar", "d.jar", "e.jar", "f.jar", "g.jar", "h.jar", "i.jar",
+ "j.jar");
+ ImmutableMap<String, String> directJarsToTargets =
+ ImmutableMap.of(
+ "c.jar", "//a",
+ "d.jar", "//d",
+ "g.jar", "//e");
+ ImmutableList<String> depsArtifacts =
+ ImmutableList.of(cdeps.toString(), ddeps.toString(), gdeps.toString());
+ writeDeps(
+ cdeps,
+ ImmutableMap.of(
+ "b.jar", DepsProto.Dependency.Kind.EXPLICIT,
+ "e.jar", DepsProto.Dependency.Kind.EXPLICIT));
+ writeDeps(
+ ddeps,
+ ImmutableMap.of(
+ "f.jar", DepsProto.Dependency.Kind.UNUSED,
+ "j.jar", DepsProto.Dependency.Kind.UNUSED));
+ writeDeps(gdeps, ImmutableMap.of("i.jar", DepsProto.Dependency.Kind.IMPLICIT));
+ assertThat(Dependencies.reduceClasspath(classpath, directJarsToTargets, depsArtifacts))
+ .containsExactly("b.jar", "c.jar", "d.jar", "e.jar", "g.jar", "i.jar")
+ .inOrder();
+ }
+
+ void writeDeps(Path path, ImmutableMap<String, DepsProto.Dependency.Kind> deps)
+ throws IOException {
+ DepsProto.Dependencies.Builder builder =
+ DepsProto.Dependencies.newBuilder().setSuccess(true).setRuleLabel("//test");
+ for (Map.Entry<String, DepsProto.Dependency.Kind> e : deps.entrySet()) {
+ builder.addDependency(
+ DepsProto.Dependency.newBuilder().setPath(e.getKey()).setKind(e.getValue()));
+ }
+ try (OutputStream os = new BufferedOutputStream(Files.newOutputStream(path))) {
+ builder.build().writeTo(os);
+ }
+ }
}