diff options
10 files changed, 284 insertions, 153 deletions
diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/BazelJavaBuilder.java b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/BazelJavaBuilder.java index 607adcd7d1..0fb8456212 100644 --- a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/BazelJavaBuilder.java +++ b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/BazelJavaBuilder.java @@ -16,6 +16,8 @@ package com.google.devtools.build.buildjar; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; +import com.google.devtools.build.buildjar.javac.BlazeJavacResult; +import com.google.devtools.build.buildjar.javac.FormattedDiagnostic; import com.google.devtools.build.buildjar.javac.JavacOptions; import com.google.devtools.build.buildjar.javac.plugins.BlazeJavaCompilerPlugin; import com.google.devtools.build.buildjar.javac.plugins.dependency.DependencyModule; @@ -85,7 +87,12 @@ public abstract class BazelJavaBuilder { build.getDependencyModule().reduceClasspath() ? new ReducedClasspathJavaLibraryBuilder() : new SimpleJavaLibraryBuilder()) { - return builder.run(build, err).exitCode; + BlazeJavacResult result = builder.run(build); + for (FormattedDiagnostic d : result.diagnostics()) { + err.write(d.getFormatted()); + } + err.write(result.output()); + return result.javacResult().exitCode; } } catch (InvalidCommandLineException e) { err.println(CMDNAME + " threw exception: " + e.getMessage()); 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 df09cf055b..ece6484ba2 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 @@ -295,6 +295,7 @@ public final class JavaLibraryBuildRequest { .processors(null) .sourceOutput(getSourceGenDir() != null ? Paths.get(getSourceGenDir()) : null) .processorPath(toPaths(getProcessorPath())) + .plugins(getPlugins()) .build(); } diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/ReducedClasspathJavaLibraryBuilder.java b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/ReducedClasspathJavaLibraryBuilder.java index 8e29cf1644..507e2eba5b 100644 --- a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/ReducedClasspathJavaLibraryBuilder.java +++ b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/ReducedClasspathJavaLibraryBuilder.java @@ -14,11 +14,11 @@ package com.google.devtools.build.buildjar; +import com.google.common.collect.ImmutableList; +import com.google.devtools.build.buildjar.javac.BlazeJavacResult; +import com.google.devtools.build.buildjar.javac.FormattedDiagnostic; import com.google.devtools.build.buildjar.javac.JavacRunner; -import com.sun.tools.javac.main.Main.Result; import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; import java.util.regex.Pattern; /** @@ -36,11 +36,10 @@ public class ReducedClasspathJavaLibraryBuilder extends SimpleJavaLibraryBuilder * regular compile. * * @param build A JavaLibraryBuildRequest request object describing what to compile - * @return result code of the javac compilation * @throws IOException clean-up up the output directory fails */ @Override - Result compileSources(JavaLibraryBuildRequest build, JavacRunner javacRunner, PrintWriter err) + BlazeJavacResult compileSources(JavaLibraryBuildRequest build, JavacRunner javacRunner) throws IOException { // Minimize classpath, but only if we're actually compiling some sources (some invocations of // JavaBuilder are only building resource jars). @@ -58,31 +57,19 @@ public class ReducedClasspathJavaLibraryBuilder extends SimpleJavaLibraryBuilder } // Compile! - StringWriter javacOutput = new StringWriter(); - PrintWriter javacOutputWriter = new PrintWriter(javacOutput); - Result result = - javacRunner.invokeJavac( - build.getPlugins(), - build.toBlazeJavacArguments(compressedClasspath), - javacOutputWriter); - javacOutputWriter.close(); + BlazeJavacResult result = + javacRunner.invokeJavac(build.toBlazeJavacArguments(compressedClasspath)); // If javac errored out because of missing entries on the classpath, give it another try. // TODO(bazel-team): check performance impact of additional retries. - if (!result.isOK() && hasRecognizedError(javacOutput.toString())) { - if (debug) { - err.println("warning: [transitive] Target uses transitive classpath to compile."); - } + if (!result.javacResult().isOK() && hasRecognizedError(result.diagnostics())) { + // TODO(cushon): warn for transitive classpath fallback // Reset output directories prepareSourceCompilation(build); // Fall back to the regular compile, but add extra checks to catch transitive uses - result = - javacRunner.invokeJavac( - build.getPlugins(), build.toBlazeJavacArguments(build.getClassPath()), err); - } else { - err.print(javacOutput.getBuffer()); + result = javacRunner.invokeJavac(build.toBlazeJavacArguments(build.getClassPath())); } return result; } @@ -90,12 +77,17 @@ public class ReducedClasspathJavaLibraryBuilder extends SimpleJavaLibraryBuilder private static final Pattern MISSING_PACKAGE = Pattern.compile("error: package ([\\p{javaJavaIdentifierPart}\\.]+) does not exist"); - private boolean hasRecognizedError(String javacOutput) { - return javacOutput.contains("error: cannot access") - || javacOutput.contains("error: cannot find symbol") - || javacOutput.contains("com.sun.tools.javac.code.Symbol$CompletionFailure") - || MISSING_PACKAGE.matcher(javacOutput).find() - // TODO(cushon): -Xdoclint:reference is probably a bad idea - || javacOutput.contains("error: reference not found"); + private boolean hasRecognizedError(ImmutableList<FormattedDiagnostic> diagnostics) { + // TODO(cushon): usage diagnostic codes instead + for (FormattedDiagnostic diagnostic : diagnostics) { + String message = diagnostic.getFormatted(); + return message.contains("error: cannot access") + || message.contains("error: cannot find symbol") + || message.contains("com.sun.tools.javac.code.Symbol$CompletionFailure") + || MISSING_PACKAGE.matcher(message).find() + // TODO(cushon): -Xdoclint:reference is probably a bad idea + || message.contains("error: reference not found"); + } + return false; } } diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/SimpleJavaLibraryBuilder.java b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/SimpleJavaLibraryBuilder.java index 96c9688196..858c884b3e 100644 --- a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/SimpleJavaLibraryBuilder.java +++ b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/SimpleJavaLibraryBuilder.java @@ -15,20 +15,17 @@ package com.google.devtools.build.buildjar; import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.ImmutableList; import com.google.devtools.build.buildjar.instrumentation.JacocoInstrumentationProcessor; import com.google.devtools.build.buildjar.jarhelper.JarCreator; import com.google.devtools.build.buildjar.javac.BlazeJavacArguments; import com.google.devtools.build.buildjar.javac.BlazeJavacMain; +import com.google.devtools.build.buildjar.javac.BlazeJavacResult; import com.google.devtools.build.buildjar.javac.JavacRunner; -import com.google.devtools.build.buildjar.javac.plugins.BlazeJavaCompilerPlugin; -import com.sun.tools.javac.main.Main.Result; import java.io.ByteArrayOutputStream; import java.io.Closeable; import java.io.File; import java.io.IOException; import java.io.OutputStream; -import java.io.PrintWriter; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.FileVisitResult; @@ -48,9 +45,6 @@ public class SimpleJavaLibraryBuilder implements Closeable { /** The name of the protobuf meta file. */ private static final String PROTOBUF_META_NAME = "protobuf.meta"; - /** Enables more verbose output from the compiler. */ - protected boolean debug = false; - /** Cache of opened zip filesystems for srcjars. */ private final Map<Path, FileSystem> filesystems = new HashMap<>(); @@ -110,10 +104,9 @@ public class SimpleJavaLibraryBuilder implements Closeable { } } - Result compileSources(JavaLibraryBuildRequest build, JavacRunner javacRunner, PrintWriter err) + BlazeJavacResult compileSources(JavaLibraryBuildRequest build, JavacRunner javacRunner) throws IOException { - return javacRunner.invokeJavac( - build.getPlugins(), build.toBlazeJavacArguments(build.getClassPath()), err); + return javacRunner.invokeJavac(build.toBlazeJavacArguments(build.getClassPath())); } protected void prepareSourceCompilation(JavaLibraryBuildRequest build) throws IOException { @@ -157,25 +150,20 @@ public class SimpleJavaLibraryBuilder implements Closeable { * can be compiled. Invokes compileSources to do the actual compilation. * * @param build A JavaLibraryBuildRequest request object describing what to compile - * @param err PrintWriter for logging any diagnostic output */ - public Result compileJavaLibrary(final JavaLibraryBuildRequest build, final PrintWriter err) - throws Exception { + public BlazeJavacResult compileJavaLibrary(final JavaLibraryBuildRequest build) throws Exception { prepareSourceCompilation(build); if (build.getSourceFiles().isEmpty()) { - return Result.OK; + return BlazeJavacResult.ok(); } JavacRunner javacRunner = new JavacRunner() { @Override - public Result invokeJavac( - ImmutableList<BlazeJavaCompilerPlugin> plugins, - BlazeJavacArguments arguments, - PrintWriter output) { - return new BlazeJavacMain(output, plugins).compile(arguments); + public BlazeJavacResult invokeJavac(BlazeJavacArguments arguments) { + return BlazeJavacMain.compile(arguments); } }; - Result result = compileSources(build, javacRunner, err); + BlazeJavacResult result = compileSources(build, javacRunner); JacocoInstrumentationProcessor processor = build.getJacocoInstrumentationProcessor(); if (processor != null) { processor.processRequest(build); @@ -184,11 +172,11 @@ public class SimpleJavaLibraryBuilder implements Closeable { } /** Perform the build. */ - public Result run(JavaLibraryBuildRequest build, PrintWriter err) throws Exception { - Result result = Result.ERROR; + public BlazeJavacResult run(JavaLibraryBuildRequest build) throws Exception { + BlazeJavacResult result = BlazeJavacResult.error(""); try { - result = compileJavaLibrary(build, err); - if (result.isOK()) { + result = compileJavaLibrary(build); + if (result.javacResult().isOK()) { buildJar(build); } if (!build.getProcessors().isEmpty()) { @@ -197,7 +185,9 @@ public class SimpleJavaLibraryBuilder implements Closeable { } } } finally { - build.getDependencyModule().emitDependencyInformation(build.getClassPath(), result.isOK()); + build + .getDependencyModule() + .emitDependencyInformation(build.getClassPath(), result.javacResult().isOK()); build.getProcessingModule().emitManifestProto(); } return result; diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/javac/BlazeJavaCompiler.java b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/javac/BlazeJavaCompiler.java index 7cee54b0f8..d5db1e5fc4 100644 --- a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/javac/BlazeJavaCompiler.java +++ b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/javac/BlazeJavaCompiler.java @@ -14,7 +14,6 @@ package com.google.devtools.build.buildjar.javac; -import com.google.common.base.Function; import com.google.devtools.build.buildjar.javac.plugins.BlazeJavaCompilerPlugin; import com.sun.tools.javac.comp.AttrContext; import com.sun.tools.javac.comp.CompileStates.CompileState; @@ -24,7 +23,6 @@ import com.sun.tools.javac.util.Context; import java.util.ArrayList; import java.util.List; import java.util.Queue; -import javax.annotation.Nullable; /** * An extended version of the javac compiler, providing support for composable static analyses via a @@ -63,9 +61,7 @@ public class BlazeJavaCompiler extends JavaCompiler { * and save the output after its call for introspection. */ public static void preRegister( - final Context context, - final Iterable<BlazeJavaCompilerPlugin> plugins, - @Nullable final Function<BlazeJavaCompiler, Void> listener) { + final Context context, final Iterable<BlazeJavaCompilerPlugin> plugins) { context.put( compilerKey, new Context.Factory<JavaCompiler>() { @@ -77,20 +73,11 @@ public class BlazeJavaCompiler extends JavaCompiler { throw new AssertionError("Expected a single creation of BlazeJavaCompiler."); } first = false; - BlazeJavaCompiler compiler = new BlazeJavaCompiler(c, plugins); - if (listener != null) { - listener.apply(compiler); - } - return compiler; + return new BlazeJavaCompiler(c, plugins); } }); } - public static void preRegister( - final Context context, final Iterable<BlazeJavaCompilerPlugin> plugins) { - preRegister(context, plugins, null); - } - @Override public Env<AttrContext> attribute(Env<AttrContext> env) { Env<AttrContext> result = super.attribute(env); diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/javac/BlazeJavacArguments.java b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/javac/BlazeJavacArguments.java index 7554ddc2e1..37c96e8ca8 100644 --- a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/javac/BlazeJavacArguments.java +++ b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/javac/BlazeJavacArguments.java @@ -16,6 +16,7 @@ package com.google.devtools.build.buildjar.javac; import com.google.auto.value.AutoValue; import com.google.common.collect.ImmutableList; +import com.google.devtools.build.buildjar.javac.plugins.BlazeJavaCompilerPlugin; import java.nio.file.Path; import javax.annotation.Nullable; import javax.annotation.processing.Processor; @@ -44,6 +45,9 @@ public abstract class BlazeJavacArguments { /** The classpath to load processors from. */ public abstract ImmutableList<Path> processorPath(); + /** The compiler plugins. */ + public abstract ImmutableList<BlazeJavaCompilerPlugin> plugins(); + /** * Annotation processor classes. In production builds, processors are specified by string class * name in {@link javacOptions}; this is used for tests that instantate processors directly. @@ -67,7 +71,8 @@ public abstract class BlazeJavacArguments { .sourceFiles(ImmutableList.of()) .processors(null) .sourceOutput(null) - .processorPath(ImmutableList.of()); + .processorPath(ImmutableList.of()) + .plugins(ImmutableList.of()); } /** {@link BlazeJavacArguments}Builder. */ @@ -89,6 +94,8 @@ public abstract class BlazeJavacArguments { Builder processorPath(ImmutableList<Path> processorPath); + Builder plugins(ImmutableList<BlazeJavaCompilerPlugin> plugins); + BlazeJavacArguments build(); } } diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/javac/BlazeJavacMain.java b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/javac/BlazeJavacMain.java index ecd1161baa..fae6e2c111 100644 --- a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/javac/BlazeJavacMain.java +++ b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/javac/BlazeJavacMain.java @@ -14,34 +14,32 @@ package com.google.devtools.build.buildjar.javac; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Verify.verifyNotNull; import static java.nio.charset.StandardCharsets.UTF_8; import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Function; -import com.google.common.base.Verify; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.devtools.build.buildjar.InvalidCommandLineException; +import com.google.devtools.build.buildjar.javac.FormattedDiagnostic.Listener; import com.google.devtools.build.buildjar.javac.plugins.BlazeJavaCompilerPlugin; import com.google.devtools.build.buildjar.javac.plugins.BlazeJavaCompilerPlugin.PluginException; +import com.sun.source.util.JavacTask; import com.sun.tools.javac.api.ClientCodeWrapper.Trusted; import com.sun.tools.javac.api.JavacTaskImpl; import com.sun.tools.javac.api.JavacTool; import com.sun.tools.javac.file.JavacFileManager; +import com.sun.tools.javac.main.JavaCompiler; import com.sun.tools.javac.main.Main.Result; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.PropagatedException; import java.io.IOError; import java.io.IOException; import java.io.PrintWriter; +import java.io.StringWriter; import java.net.URL; import java.net.URLClassLoader; import java.nio.file.Path; import java.util.List; -import javax.tools.DiagnosticListener; -import javax.tools.JavaFileObject; import javax.tools.StandardLocation; /** @@ -54,77 +52,48 @@ import javax.tools.StandardLocation; public class BlazeJavacMain { /** - * Compose {@link com.sun.tools.javac.main.Main} and perform custom setup before deferring to its - * compile() method. - * - * <p>Historically BlazeJavacMain extended javac's Main and overrode methods to get the desired - * custom behaviour. That approach created incompatibilities when upgrading to newer versions of - * javac, so composition is preferred. - */ - private List<BlazeJavaCompilerPlugin> plugins; - - private final PrintWriter errOutput; - private BlazeJavaCompiler compiler = null; - - public BlazeJavacMain(PrintWriter errOutput, List<BlazeJavaCompilerPlugin> plugins) { - this.errOutput = errOutput; - this.plugins = plugins; - } - - /** - * Installs the BlazeJavaCompiler within the provided context. Enables plugins based on field - * values. + * Sets up a BlazeJavaCompiler with the given plugins within the given context. * * @param context JavaCompiler's associated Context */ @VisibleForTesting - void setupBlazeJavaCompiler(Context context) { + static void setupBlazeJavaCompiler( + ImmutableList<BlazeJavaCompilerPlugin> plugins, Context context) { for (BlazeJavaCompilerPlugin plugin : plugins) { plugin.initializeContext(context); } - BlazeJavaCompiler.preRegister(context, plugins, compilerListener); + BlazeJavaCompiler.preRegister(context, plugins); } - private final Function<BlazeJavaCompiler, Void> compilerListener = - new Function<BlazeJavaCompiler, Void>() { - @Override - public Void apply(BlazeJavaCompiler compiler) { - Verify.verify(BlazeJavacMain.this.compiler == null); - BlazeJavacMain.this.compiler = checkNotNull(compiler); - return null; - } - }; - - public Result compile(BlazeJavacArguments arguments) { - return compile(null, arguments); - } - - public Result compile( - DiagnosticListener<JavaFileObject> diagnosticListener, BlazeJavacArguments arguments) { - - JavacFileManager fileManager = new ClassloaderMaskingFileManager(); + public static BlazeJavacResult compile(BlazeJavacArguments arguments) { List<String> javacArguments = arguments.javacOptions(); try { - javacArguments = processPluginArgs(javacArguments); + javacArguments = processPluginArgs(arguments.plugins(), javacArguments); } catch (InvalidCommandLineException e) { - errOutput.println(e.getMessage()); - return Result.CMDERR; + return BlazeJavacResult.error(e.getMessage()); } Context context = new Context(); - setupBlazeJavaCompiler(context); + setupBlazeJavaCompiler(arguments.plugins(), context); Result result = Result.ABNORMAL; - JavacTool tool = JavacTool.create(); - JavacTaskImpl task = - (JavacTaskImpl) - tool.getTask( - errOutput, + StringWriter errOutput = new StringWriter(); + // TODO(cushon): where is this used when a diagnostic listener is registered? Consider removing + // it and handling exceptions directly in callers. + PrintWriter errWriter = new PrintWriter(errOutput); + Listener diagnostics = new Listener(context); + BlazeJavaCompiler compiler; + + JavacFileManager fileManager = new ClassloaderMaskingFileManager(); + JavacTask task = + JavacTool.create() + .getTask( + errWriter, fileManager, - diagnosticListener, + diagnostics, javacArguments, - ImmutableList.<String>of() /*classes*/, + ImmutableList.of() /*classes*/, fileManager.getJavaFileObjectsFromPaths(arguments.sourceFiles()), context); if (arguments.processors() != null) { @@ -134,36 +103,39 @@ public class BlazeJavacMain { setLocations(fileManager, arguments); try { try { - result = task.doCall(); + result = ((JavacTaskImpl) task).doCall(); } catch (PropagatedException e) { throw e.getCause(); } } catch (PluginException e) { - errOutput.println(e.getMessage()); + errWriter.println(e.getMessage()); result = e.getResult(); } catch (Throwable t) { - t.printStackTrace(errOutput); + t.printStackTrace(errWriter); result = Result.ABNORMAL; } finally { + compiler = (BlazeJavaCompiler) JavaCompiler.instance(context); if (result.isOK()) { - verifyNotNull(compiler); // There could be situations where we incorrectly skip Error Prone and the compilation // ends up succeeding, e.g., if there are errors that are fixed by subsequent round of // annotation processing. This check ensures that if there were any flow events at all, // then plugins were run. There may legitimately not be any flow events, e.g. -proc:only // or empty source files. if (compiler.skippedFlowEvents() > 0 && compiler.flowEvents() == 0) { - errOutput.println("Expected at least one FLOW event"); + errWriter.println("Expected at least one FLOW event"); result = Result.ABNORMAL; } } } - return result; + errWriter.flush(); + return new BlazeJavacResult(result, diagnostics.build(), errOutput.toString(), compiler); } /** Processes Plugin-specific arguments and removes them from the args array. */ @VisibleForTesting - List<String> processPluginArgs(List<String> args) throws InvalidCommandLineException { + static List<String> processPluginArgs( + ImmutableList<BlazeJavaCompilerPlugin> plugins, List<String> args) + throws InvalidCommandLineException { List<String> processedArgs = args; for (BlazeJavaCompilerPlugin plugin : plugins) { processedArgs = plugin.processArgs(processedArgs); @@ -171,17 +143,13 @@ public class BlazeJavacMain { return processedArgs; } - @VisibleForTesting - BlazeJavaCompiler getCompiler() { - return verifyNotNull(compiler); - } - - private void setLocations(JavacFileManager fileManager, BlazeJavacArguments arguments) { + private static void setLocations(JavacFileManager fileManager, BlazeJavacArguments arguments) { try { fileManager.setLocationFromPaths(StandardLocation.CLASS_PATH, arguments.classPath()); fileManager.setLocationFromPaths( StandardLocation.CLASS_OUTPUT, ImmutableList.of(arguments.classOutput())); - fileManager.setLocationFromPaths(StandardLocation.SOURCE_PATH, ImmutableList.<Path>of()); + fileManager.setLocationFromPaths(StandardLocation.SOURCE_PATH, ImmutableList.of()); + // TODO(cushon): require an explicit bootclasspath Iterable<Path> bootClassPath = arguments.bootClassPath(); if (!Iterables.isEmpty(bootClassPath)) { fileManager.setLocationFromPaths(StandardLocation.PLATFORM_CLASS_PATH, bootClassPath); @@ -190,7 +158,7 @@ public class BlazeJavacMain { StandardLocation.ANNOTATION_PROCESSOR_PATH, arguments.processorPath()); if (arguments.sourceOutput() != null) { fileManager.setLocationFromPaths( - StandardLocation.SOURCE_OUTPUT, ImmutableList.<Path>of(arguments.sourceOutput())); + StandardLocation.SOURCE_OUTPUT, ImmutableList.of(arguments.sourceOutput())); } } catch (IOException e) { throw new IOError(e); @@ -228,4 +196,6 @@ public class BlazeJavacMain { }); } } + + private BlazeJavacMain() {} } diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/javac/BlazeJavacResult.java b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/javac/BlazeJavacResult.java new file mode 100644 index 0000000000..61702ffa09 --- /dev/null +++ b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/javac/BlazeJavacResult.java @@ -0,0 +1,64 @@ +// 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.buildjar.javac; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableList; +import com.sun.tools.javac.main.Main.Result; + +/** The result of a single compilation performed by {@link BlazeJavacMain}. */ +public class BlazeJavacResult { + + private final Result javacResult; + private final ImmutableList<FormattedDiagnostic> diagnostics; + private final String output; + private final BlazeJavaCompiler compiler; + + public static BlazeJavacResult ok() { + return new BlazeJavacResult(Result.OK, ImmutableList.of(), "", null); + } + + public static BlazeJavacResult error(String message) { + return new BlazeJavacResult(Result.ERROR, ImmutableList.of(), message, null); + } + + public BlazeJavacResult( + Result javacResult, + ImmutableList<FormattedDiagnostic> diagnostics, + String output, + BlazeJavaCompiler compiler) { + this.javacResult = javacResult; + this.diagnostics = diagnostics; + this.output = output; + this.compiler = compiler; + } + + public Result javacResult() { + return javacResult; + } + + public ImmutableList<FormattedDiagnostic> diagnostics() { + return diagnostics; + } + + public String output() { + return output; + } + + @VisibleForTesting + public BlazeJavaCompiler compiler() { + return compiler; + } +} diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/javac/FormattedDiagnostic.java b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/javac/FormattedDiagnostic.java new file mode 100644 index 0000000000..477d7cee50 --- /dev/null +++ b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/javac/FormattedDiagnostic.java @@ -0,0 +1,122 @@ +// 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.buildjar.javac; + +import com.google.common.collect.ImmutableList; +import com.sun.tools.javac.api.ClientCodeWrapper.Trusted; +import com.sun.tools.javac.api.DiagnosticFormatter; +import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.JCDiagnostic; +import com.sun.tools.javac.util.JavacMessages; +import com.sun.tools.javac.util.Log; +import java.util.Locale; +import javax.tools.Diagnostic; +import javax.tools.DiagnosticListener; +import javax.tools.JavaFileObject; + +/** + * A {@link Diagnostic<JavaFileObject>} that includes the full formatted message produced by javac, + * which relies on compilation internals and can't be reproduced after the compilation is complete. + */ +public class FormattedDiagnostic implements Diagnostic<JavaFileObject> { + + public final Diagnostic<? extends JavaFileObject> diagnostic; + public final String formatted; + + public FormattedDiagnostic(Diagnostic<? extends JavaFileObject> diagnostic, String formatted) { + this.diagnostic = diagnostic; + this.formatted = formatted; + } + + /** The formatted diagnostic message produced by javac's diagnostic formatter. */ + public String getFormatted() { + return formatted; + } + + @Override + public String toString() { + return formatted; + } + + @Override + public Kind getKind() { + return diagnostic.getKind(); + } + + @Override + public JavaFileObject getSource() { + return diagnostic.getSource(); + } + + @Override + public long getPosition() { + return diagnostic.getPosition(); + } + + @Override + public long getStartPosition() { + return diagnostic.getStartPosition(); + } + + @Override + public long getEndPosition() { + return diagnostic.getEndPosition(); + } + + @Override + public long getLineNumber() { + return diagnostic.getLineNumber(); + } + + @Override + public long getColumnNumber() { + return diagnostic.getColumnNumber(); + } + + @Override + public String getCode() { + return diagnostic.getCode(); + } + + @Override + public String getMessage(Locale locale) { + return diagnostic.getMessage(locale); + } + + /** A {@link DiagnosticListener<JavaFileObject>} that saves {@link FormattedDiagnostic}s. */ + @Trusted + static class Listener implements DiagnosticListener<JavaFileObject> { + + private final ImmutableList.Builder<FormattedDiagnostic> diagnostics = ImmutableList.builder(); + private final Context context; + + Listener(Context context) { + // retrieve context values later, in case it isn't initialized yet + this.context = context; + } + + @Override + public void report(Diagnostic<? extends JavaFileObject> diagnostic) { + DiagnosticFormatter<JCDiagnostic> formatter = Log.instance(context).getDiagnosticFormatter(); + Locale locale = JavacMessages.instance(context).getCurrentLocale(); + String formatted = formatter.format((JCDiagnostic) diagnostic, locale); + diagnostics.add(new FormattedDiagnostic(diagnostic, formatted)); + } + + ImmutableList<FormattedDiagnostic> build() { + return diagnostics.build(); + } + } +} diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/javac/JavacRunner.java b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/javac/JavacRunner.java index 11f72b2f2d..6505b32dec 100644 --- a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/javac/JavacRunner.java +++ b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/javac/JavacRunner.java @@ -14,19 +14,10 @@ package com.google.devtools.build.buildjar.javac; -import com.google.common.collect.ImmutableList; -import com.google.devtools.build.buildjar.javac.plugins.BlazeJavaCompilerPlugin; -import com.sun.tools.javac.main.Main.Result; -import java.io.PrintWriter; - /** * The JavacRunner is a type that can be used to invoke javac and provides a convenient hook for * modifications. */ public interface JavacRunner { - - Result invokeJavac( - ImmutableList<BlazeJavaCompilerPlugin> plugins, - BlazeJavacArguments arguments, - PrintWriter err); + BlazeJavacResult invokeJavac(BlazeJavacArguments arguments); } |