diff options
Diffstat (limited to 'src/java_tools/buildjar/java/com/google/devtools/build/buildjar/javac/BlazeJavacMain.java')
-rw-r--r-- | src/java_tools/buildjar/java/com/google/devtools/build/buildjar/javac/BlazeJavacMain.java | 202 |
1 files changed, 202 insertions, 0 deletions
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 new file mode 100644 index 0000000000..92835880db --- /dev/null +++ b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/javac/BlazeJavacMain.java @@ -0,0 +1,202 @@ +// Copyright 2014 Google Inc. 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.devtools.build.buildjar.InvalidCommandLineException; +import com.google.devtools.build.buildjar.javac.plugins.BlazeJavaCompilerPlugin; +import com.google.devtools.build.buildjar.javac.plugins.BlazeJavaCompilerPlugin.PluginException; + +import com.sun.source.util.TaskEvent; +import com.sun.source.util.TaskListener; +import com.sun.tools.javac.api.JavacTaskImpl; +import com.sun.tools.javac.api.JavacTool; +import com.sun.tools.javac.api.MultiTaskListener; +import com.sun.tools.javac.main.Main; +import com.sun.tools.javac.main.Main.Result; +import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.Options; +import com.sun.tools.javac.util.PropagatedException; + +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.List; + +import javax.annotation.processing.Processor; +import javax.tools.DiagnosticListener; +import javax.tools.JavaFileManager; +import javax.tools.JavaFileObject; + +/** + * Main class for our custom patched javac. + * + * <p> This main class tweaks the standard javac log class by changing the + * compiler's context to use our custom log class. This custom log class + * modifies javac's output to list all errors after all warnings. + */ +public class BlazeJavacMain { + + /** + * Compose {@link com.sun.tools.javac.main.Main} and perform custom setup before deferring to + * its compile() method. + * + * 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. + */ + @VisibleForTesting + private List<BlazeJavaCompilerPlugin> plugins; + private final PrintWriter errOutput; + private final String compilerName; + + public BlazeJavacMain(PrintWriter errOutput, List<BlazeJavaCompilerPlugin> plugins) { + this.compilerName = "blaze javac"; + this.errOutput = errOutput; + this.plugins = plugins; + } + + /** + * Installs the BlazeJavaCompiler within the provided context. Enables + * plugins based on field values. + * + * @param context JavaCompiler's associated Context + */ + void setupBlazeJavaCompiler(Context context) { + preRegister(context, plugins); + } + + public void preRegister(Context context, List<BlazeJavaCompilerPlugin> plugins) { + this.plugins = plugins; + for (BlazeJavaCompilerPlugin plugin : plugins) { + plugin.initializeContext(context); + } + + BlazeJavacLog.preRegister(context); + BlazeJavaCompiler.preRegister(context, plugins); + } + + public Result compile(String[] argv) { + // set up a fresh Context with our custom bindings for JavaCompiler + Context context = new Context(); + // disable faulty Zip optimizations + Options options = Options.instance(context); + options.put("useOptimizedZip", "false"); + + // enable Java 8-style type inference features + // + // This is currently duplicated in JAVABUILDER. That's deliberate for now, because + // (1) JavaBuilder's integration test coverage for default options isn't very good, and + // (2) the migration from JAVABUILDER to java_toolchain configs is in progress so blaze + // integration tests for defaults options are also not trustworthy. + // + // TODO(bazel-team): removed duplication with JAVABUILDER + options.put("usePolyAttribution", "true"); + options.put("useStrictMethodClashCheck", "true"); + options.put("useStructuralMostSpecificResolution", "true"); + options.put("useGraphInference", "true"); + + String[] processedArgs; + + try { + processedArgs = processPluginArgs(argv); + } catch (InvalidCommandLineException e) { + errOutput.println(e.getMessage()); + return Result.CMDERR; + } + + setupBlazeJavaCompiler(context); + return compile(processedArgs, context); + } + + @VisibleForTesting + public Result compile(String[] argv, Context context) { + enableEndPositions(context); + try { + return new Main(compilerName, errOutput).compile(argv, context); + } catch (PropagatedException e) { + if (e.getCause() instanceof PluginException) { + PluginException pluginException = (PluginException) e.getCause(); + errOutput.println(pluginException.getMessage()); + return pluginException.getResult(); + } + e.printStackTrace(errOutput); + return Result.ABNORMAL; + } + } + + // javac9 removes the ability to pass lists of {@link JavaFileObject}s or {@link Processors}s to + // it's 'Main' class (i.e. the entry point for command-line javac). Having BlazeJavacMain + // continue to call into javac's Main has the nice property that it keeps JavaBuilder's + // behaviour closer to stock javac, but it makes it harder to write integration tests. This class + // provides a compile method that accepts file objects and processors, but it isn't + // guaranteed to behave exactly the same way as JavaBuilder does when used from the command-line. + // TODO(bazel-team): either stop using Main and commit to using the the API for everything, or + // re-write integration tests for JavaBuilder to use the real compile() method. + @VisibleForTesting + @Deprecated + public Result compile( + String[] argv, + Context context, + JavaFileManager fileManager, + DiagnosticListener<? super JavaFileObject> diagnosticListener, + List<JavaFileObject> javaFileObjects, + Iterable<? extends Processor> processors) { + + JavacTool tool = JavacTool.create(); + JavacTaskImpl task = (JavacTaskImpl) tool.getTask( + errOutput, + fileManager, + diagnosticListener, + Arrays.asList(argv), + null, + javaFileObjects, + context); + if (processors != null) { + task.setProcessors(processors); + } + + try { + return task.doCall(); + } catch (PluginException e) { + errOutput.println(e.getMessage()); + return e.getResult(); + } + } + + private static final TaskListener EMPTY_LISTENER = new TaskListener() { + @Override public void started(TaskEvent e) {} + @Override public void finished(TaskEvent e) {} + }; + + /** + * Convinces javac to run in 'API mode', and collect end position information needed by + * error-prone. + */ + private static void enableEndPositions(Context context) { + MultiTaskListener.instance(context).add(EMPTY_LISTENER); + } + + /** + * Processes Plugin-specific arguments and removes them from the args array. + */ + @VisibleForTesting + String[] processPluginArgs(String[] args) throws InvalidCommandLineException { + List<String> processedArgs = Arrays.asList(args); + for (BlazeJavaCompilerPlugin plugin : plugins) { + processedArgs = plugin.processArgs(processedArgs); + } + return processedArgs.toArray(new String[processedArgs.size()]); + } +} |