Skip to content

Commit

Permalink
Support direct classpaths in javac-turbine
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 167520688
  • Loading branch information
cushon authored and meteorcloudy committed Sep 5, 2017
1 parent fd4f5ac commit 98bfd98
Show file tree
Hide file tree
Showing 9 changed files with 263 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@
import java.util.Properties;
import java.util.Set;
import javax.annotation.Generated;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;

/**
Expand Down Expand Up @@ -77,7 +76,6 @@ public final class StrictJavaDepsPlugin extends BlazeJavaCompilerPlugin {

private static Properties targetMap;

private JavaFileManager fileManager;

private PrintWriter errWriter;

Expand All @@ -103,7 +101,6 @@ public StrictJavaDepsPlugin(DependencyModule dependencyModule) {
public void init(Context context, Log log, JavaCompiler compiler) {
super.init(context, log, compiler);
errWriter = log.getWriter(WriterKind.ERROR);
fileManager = context.get(JavaFileManager.class);
implicitDependencyExtractor =
new ImplicitDependencyExtractor(
dependencyModule.getUsedClasspath(),
Expand All @@ -113,7 +110,7 @@ public void init(Context context, Log log, JavaCompiler compiler) {
if (checkingTreeScanner == null) {
Set<String> platformJars = dependencyModule.getPlatformJars();
checkingTreeScanner =
new CheckingTreeScanner(dependencyModule, log, missingTargets, platformJars, fileManager);
new CheckingTreeScanner(dependencyModule, log, missingTargets, platformJars);
context.put(CheckingTreeScanner.class, checkingTreeScanner);
}
initTargetMap();
Expand Down Expand Up @@ -202,9 +199,6 @@ private static class CheckingTreeScanner extends TreeScanner {
/** All error reporting is done through javac's log, */
private final Log log;

/** The compilation's file manager. */
private final JavaFileManager fileManager;

/** The strict_java_deps mode */
private final StrictJavaDeps strictJavaDepsMode;

Expand All @@ -229,15 +223,13 @@ public CheckingTreeScanner(
DependencyModule dependencyModule,
Log log,
Set<JarOwner> missingTargets,
Set<String> platformJars,
JavaFileManager fileManager) {
Set<String> platformJars) {
this.indirectJarsToTargets = dependencyModule.getIndirectMapping();
this.strictJavaDepsMode = dependencyModule.getStrictJavaDeps();
this.log = log;
this.missingTargets = missingTargets;
this.directDependenciesMap = dependencyModule.getExplicitDependenciesMap();
this.platformJars = platformJars;
this.fileManager = fileManager;
}

Set<ClassSymbol> getSeenClasses() {
Expand All @@ -251,7 +243,7 @@ private void checkTypeLiteral(JCTree node) {
}

Symbol.TypeSymbol sym = node.type.tsym;
String jarName = getJarName(fileManager, sym.enclClass(), platformJars);
String jarName = getJarName(sym.enclClass(), platformJars);

// If this type symbol comes from a class file loaded from a jar, check
// whether that jar was a direct dependency and error out otherwise.
Expand Down Expand Up @@ -418,8 +410,7 @@ static String canonicalizeTarget(String target) {
*
* @param platformJars jars on javac's bootclasspath
*/
static String getJarName(
JavaFileManager fileManager, ClassSymbol classSymbol, Set<String> platformJars) {
public static String getJarName(ClassSymbol classSymbol, Set<String> platformJars) {
if (classSymbol == null) {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ java_library(
name = "javac_turbine",
srcs = ["JavacTurbine.java"],
deps = [
":javac_transitive",
":javac_turbine_compile_request",
":javac_turbine_compile_result",
":javac_turbine_compiler",
Expand All @@ -28,6 +29,7 @@ java_library(
name = "javac_turbine_compile_request",
srcs = ["JavacTurbineCompileRequest.java"],
deps = [
":javac_transitive",
"//src/java_tools/buildjar/java/com/google/devtools/build/buildjar/javac/plugins:dependency",
"//third_party:guava",
"//third_party:jsr305",
Expand All @@ -49,6 +51,7 @@ java_library(
name = "javac_turbine_compiler",
srcs = ["JavacTurbineCompiler.java"],
deps = [
":javac_transitive",
":javac_turbine_compile_request",
":javac_turbine_compile_result",
":javac_turbine_java_compiler",
Expand All @@ -64,6 +67,7 @@ java_library(
name = "javac_turbine_java_compiler",
srcs = ["JavacTurbineJavaCompiler.java"],
deps = [
":javac_transitive",
":javac_turbine_compile_request",
":tree_pruner",
"//src/java_tools/buildjar/java/com/google/devtools/build/buildjar/javac/plugins:dependency",
Expand All @@ -72,6 +76,18 @@ java_library(
],
)

java_library(
name = "javac_transitive",
srcs = ["JavacTransitive.java"],
deps = [
"//src/java_tools/buildjar/java/com/google/devtools/build/buildjar/javac/plugins:dependency",
"//third_party:guava",
"//third_party:jsr305",
"//third_party:turbine",
"//third_party/java/jdk/langtools:javac",
],
)

java_library(
name = "zip_util",
srcs = ["ZipUtil.java"],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// Copyright 2017 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.devtools.build.java.turbine.javac;

import com.google.common.collect.ImmutableSet;
import com.google.common.io.ByteStreams;
import com.google.devtools.build.buildjar.javac.plugins.dependency.StrictJavaDepsPlugin;
import com.google.turbine.bytecode.ClassFile;
import com.google.turbine.bytecode.ClassReader;
import com.google.turbine.bytecode.ClassWriter;
import com.google.turbine.deps.Transitive;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Symbol.ClassSymbol;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.comp.AttrContext;
import com.sun.tools.javac.comp.Env;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.TreeScanner;
import java.io.IOError;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.JavaFileObject.Kind;

/**
* Collects the minimal compile-time API for symbols in the supertype closure of compiled classes.
* This allows non-javac header compilations to be performed against a classpath containing only
* direct dependencies and no transitive dependencies.
*
* <p>See {@link Transitive} for the parallel implementation in non-javac turbine.
*/
public class JavacTransitive {

private final ImmutableSet<String> platformJars;

public JavacTransitive(ImmutableSet<String> platformJars) {
this.platformJars = platformJars;
}

private final Set<ClassSymbol> closure = new LinkedHashSet<>();
private Map<String, byte[]> transitive = new LinkedHashMap<>();

/**
* Collects re-packaged transitive dependencies, and reset shared state. (The instance may be
* re-used, e.g. after fall back from the reduced classpath optimization.)
*/
public Map<String, byte[]> collectTransitiveDependencies() {
Map<String, byte[]> result = transitive;
transitive = new LinkedHashMap<>();
return result;
}

/**
* Records the super type closure of all class declarations in the given compilation unit. Called
* after attribute is complete for a compilation unit.
*/
public void postAttribute(Env<AttrContext> result) {
result.toplevel.accept(
new TreeScanner() {
@Override
public void visitClassDef(JCClassDecl tree) {
recordSuperClosure(tree.sym);
super.visitClassDef(tree);
}
});
}

/**
* Finish collecting and repackaging. Called while compilation state is still available (e.g. file
* objects are still open). This method should be idempotent, as {@link JavaCompiler}s sometimes
* get closed twice.
*/
public void finish() {
Set<ClassSymbol> directChildren = new LinkedHashSet<>();
for (ClassSymbol sym : closure) {
for (Symbol member : sym.getEnclosedElements()) {
if (member instanceof ClassSymbol) {
directChildren.add((ClassSymbol) member);
}
}
}
closure.addAll(directChildren);
for (ClassSymbol sym : closure) {
String name = sym.flatName().toString().replace('.', '/');
if (transitive.containsKey(name)) {
continue;
}
if (StrictJavaDepsPlugin.getJarName(sym, platformJars) == null) {
// Don't repackage symbols we wouldn't report in jdeps, e.g. because they're on the
// bootclasspath.
continue;
}
JavaFileObject jfo = sym.classfile;
if (jfo == null || jfo.getKind() != Kind.CLASS) {
continue;
}
ClassFile cf;
try {
cf = ClassReader.read(ByteStreams.toByteArray(jfo.openInputStream()));
} catch (IOException e) {
throw new IOError(e);
}
transitive.put(name, ClassWriter.writeClass(Transitive.trimClass(cf)));
}
closure.clear();
}

private void recordSuperClosure(Symbol bound) {
if (!(bound instanceof ClassSymbol)) {
return;
}
ClassSymbol info = (ClassSymbol) bound;
closure.add(info);
recordSuperClosure(info.getSuperclass().asElement());
for (Type i : info.getInterfaces()) {
recordSuperClosure(i.asElement());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.google.devtools.build.buildjar.javac.plugins.dependency.DependencyModule;
import com.google.devtools.build.buildjar.javac.plugins.dependency.DependencyModule.StrictJavaDeps;
import com.google.devtools.build.buildjar.javac.plugins.dependency.StrictJavaDepsPlugin;
import com.google.turbine.binder.ClassPathBinder;
import com.google.turbine.options.TurbineOptions;
import com.google.turbine.options.TurbineOptionsParser;
import com.sun.tools.javac.util.Context;
Expand Down Expand Up @@ -171,7 +172,10 @@ Result compile() throws IOException {

if (sources.isEmpty()) {
// accept compilations with an empty source list for compatibility with JavaBuilder
emitClassJar(Paths.get(turbineOptions.outputFile()), ImmutableMap.of());
emitClassJar(
Paths.get(turbineOptions.outputFile()),
/* files= */ ImmutableMap.of(),
/* transitive= */ ImmutableMap.of());
dependencyModule.emitDependencyInformation(
/*classpath=*/ ImmutableList.of(), /*successful=*/ true);
return Result.OK_WITH_REDUCED_CLASSPATH;
Expand All @@ -187,6 +191,9 @@ Result compile() throws IOException {

requestBuilder.setStrictDepsPlugin(new StrictJavaDepsPlugin(dependencyModule));

JavacTransitive transitive = new JavacTransitive(turbineOptions.bootClassPath());
requestBuilder.setTransitivePlugin(transitive);

if (turbineOptions.shouldReduceClassPath()) {
// compile with reduced classpath
actualClasspath = compressedClasspath;
Expand All @@ -211,7 +218,10 @@ Result compile() throws IOException {
}

if (result.ok()) {
emitClassJar(Paths.get(turbineOptions.outputFile()), compileResult.files());
emitClassJar(
Paths.get(turbineOptions.outputFile()),
compileResult.files(),
transitive.collectTransitiveDependencies());
dependencyModule.emitDependencyInformation(actualClasspath, compileResult.success());
} else {
out.print(compileResult.output());
Expand Down Expand Up @@ -260,11 +270,17 @@ private static JarOwner parseJarOwner(String line) {
}

/** Write the class output from a successful compilation to the output jar. */
private static void emitClassJar(Path outputJar, ImmutableMap<String, byte[]> files)
private static void emitClassJar(
Path outputJar, Map<String, byte[]> files, Map<String, byte[]> transitive)
throws IOException {
try (OutputStream fos = Files.newOutputStream(outputJar);
ZipOutputStream zipOut =
new ZipOutputStream(new BufferedOutputStream(fos, ZIPFILE_BUFFER_SIZE))) {
for (Map.Entry<String, byte[]> entry : transitive.entrySet()) {
String name = entry.getKey();
byte[] bytes = entry.getValue();
ZipUtil.storeEntry(ClassPathBinder.TRANSITIVE_PREFIX + name + ".class", bytes, zipOut);
}
for (Map.Entry<String, byte[]> entry : files.entrySet()) {
String name = entry.getKey();
byte[] bytes = entry.getValue();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,23 @@ class JavacTurbineCompileRequest {
private final ImmutableList<Path> processorClassPath;
private final ImmutableList<String> javacOptions;
@Nullable private final StrictJavaDepsPlugin strictJavaDepsPlugin;
private final JavacTransitive transitivePlugin;

JavacTurbineCompileRequest(
ImmutableList<Path> sources,
ImmutableList<Path> classPath,
ImmutableList<Path> bootClassPath,
ImmutableList<Path> processorClassPath,
ImmutableList<String> javacOptions,
@Nullable StrictJavaDepsPlugin strictJavaDepsPlugin) {
@Nullable StrictJavaDepsPlugin strictJavaDepsPlugin,
JavacTransitive transitivePlugin) {
this.sources = checkNotNull(sources);
this.classPath = checkNotNull(classPath);
this.bootClassPath = checkNotNull(bootClassPath);
this.processorClassPath = checkNotNull(processorClassPath);
this.javacOptions = checkNotNull(javacOptions);
this.strictJavaDepsPlugin = strictJavaDepsPlugin;
this.transitivePlugin = checkNotNull(transitivePlugin);
}

/** The sources to compile. */
Expand Down Expand Up @@ -79,6 +82,10 @@ StrictJavaDepsPlugin strictJavaDepsPlugin() {
return strictJavaDepsPlugin;
}

JavacTransitive transitivePlugin() {
return transitivePlugin;
}

static JavacTurbineCompileRequest.Builder builder() {
return new Builder();
}
Expand All @@ -90,12 +97,19 @@ static class Builder {
private ImmutableList<Path> processorClassPath;
private ImmutableList<String> javacOptions;
@Nullable private StrictJavaDepsPlugin strictDepsPlugin;
private JavacTransitive transitivePlugin;

private Builder() {}

JavacTurbineCompileRequest build() {
return new JavacTurbineCompileRequest(
sources, classPath, bootClassPath, processorClassPath, javacOptions, strictDepsPlugin);
sources,
classPath,
bootClassPath,
processorClassPath,
javacOptions,
strictDepsPlugin,
transitivePlugin);
}

Builder setSources(ImmutableList<Path> sources) {
Expand Down Expand Up @@ -127,5 +141,10 @@ Builder setStrictDepsPlugin(@Nullable StrictJavaDepsPlugin strictDepsPlugin) {
this.strictDepsPlugin = strictDepsPlugin;
return this;
}

Builder setTransitivePlugin(JavacTransitive transitivePlugin) {
this.transitivePlugin = transitivePlugin;
return this;
}
}
}
Loading

0 comments on commit 98bfd98

Please sign in to comment.