diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/TurbineOptions.java b/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/TurbineOptions.java index dfe78002a78b09..507cec366e2c8f 100644 --- a/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/TurbineOptions.java +++ b/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/TurbineOptions.java @@ -32,6 +32,7 @@ public class TurbineOptions { private final ImmutableList sources; private final ImmutableList processorPath; private final ImmutableSet processors; + private final ImmutableSet blacklistedProcessors; private final String tempDir; private final ImmutableList sourceJars; private final Optional outputDeps; @@ -50,6 +51,7 @@ private TurbineOptions( ImmutableList sources, ImmutableList processorPath, ImmutableSet processors, + ImmutableSet blacklistedProcessors, String tempDir, ImmutableList sourceJars, @Nullable String outputDeps, @@ -66,6 +68,8 @@ private TurbineOptions( this.sources = checkNotNull(sources, "sources must not be null"); this.processorPath = checkNotNull(processorPath, "processorPath must not be null"); this.processors = checkNotNull(processors, "processors must not be null"); + this.blacklistedProcessors = + checkNotNull(blacklistedProcessors, "blacklistedProcessors must not be null"); this.tempDir = checkNotNull(tempDir, "tempDir must not be null"); this.sourceJars = checkNotNull(sourceJars, "sourceJars must not be null"); this.outputDeps = Optional.fromNullable(outputDeps); @@ -115,6 +119,14 @@ public ImmutableSet processors() { return processors; } + /** + * Annotation processors that require tree pruning to be disabled, for example because they use + * internal compiler APIs to inspect information that would be removed during pruning. + */ + public ImmutableSet blacklistedProcessors() { + return blacklistedProcessors; + } + /** Source jars for compilation. */ public ImmutableList sourceJars() { return sourceJars; @@ -176,6 +188,7 @@ public static class Builder { private final ImmutableList.Builder sources = ImmutableList.builder(); private final ImmutableList.Builder processorPath = ImmutableList.builder(); private final ImmutableSet.Builder processors = ImmutableSet.builder(); + private final ImmutableSet.Builder blacklistedProcessors = ImmutableSet.builder(); private String tempDir; private final ImmutableList.Builder sourceJars = ImmutableList.builder(); private final ImmutableList.Builder bootClassPath = ImmutableList.builder(); @@ -197,6 +210,7 @@ public TurbineOptions build() { sources.build(), processorPath.build(), processors.build(), + blacklistedProcessors.build(), tempDir, sourceJars.build(), outputDeps, @@ -244,6 +258,11 @@ public Builder addProcessors(Iterable processors) { return this; } + public Builder addBlacklistedProcessors(Iterable processors) { + this.blacklistedProcessors.addAll(processors); + return this; + } + public Builder setTempDir(String tempDir) { this.tempDir = tempDir; return this; diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/TurbineOptionsParser.java b/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/TurbineOptionsParser.java index 2c8d87ea1c9f33..9e0ada2e2179c0 100644 --- a/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/TurbineOptionsParser.java +++ b/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/TurbineOptionsParser.java @@ -33,15 +33,24 @@ public class TurbineOptionsParser { /** - * Parses a list of command-line options into a {@link TurbineOptions}, expanding any - * {@code @params} files. + * Parses command line options into {@link TurbineOptions}, expanding any {@code @params} + * files. */ public static TurbineOptions parse(Iterable args) throws IOException { + TurbineOptions.Builder builder = TurbineOptions.builder(); + parse(builder, args); + return builder.build(); + } + + /** + * Parses command line options into a {@link TurbineOptions.Builder}, expanding any + * {@code @params} files. + */ + public static void parse(TurbineOptions.Builder builder, Iterable args) + throws IOException { Deque argumentDeque = new ArrayDeque<>(); expandParamsFiles(argumentDeque, args); - TurbineOptions.Builder builder = TurbineOptions.builder(); parse(builder, argumentDeque); - return builder.build(); } private static final Splitter ARG_SPLITTER = diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/javac/BUILD b/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/javac/BUILD index b62b34b04b392d..9e3e6cbeead2a4 100644 --- a/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/javac/BUILD +++ b/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/javac/BUILD @@ -65,6 +65,7 @@ java_library( name = "javac_turbine_java_compiler", srcs = ["JavacTurbineJavaCompiler.java"], deps = [ + ":javac_turbine_compile_request", ":tree_pruner", "//src/java_tools/buildjar/java/com/google/devtools/build/buildjar/javac/plugins:dependency", "//third_party:jsr305", diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/javac/JavacTurbine.java b/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/javac/JavacTurbine.java index 0b161158d7e8ac..0ec92b3610e3d1 100644 --- a/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/javac/JavacTurbine.java +++ b/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/javac/JavacTurbine.java @@ -24,6 +24,7 @@ import com.google.devtools.build.buildjar.javac.plugins.dependency.StrictJavaDepsPlugin; import com.google.devtools.build.java.turbine.TurbineOptions; import com.google.devtools.build.java.turbine.TurbineOptionsParser; +import com.google.devtools.build.java.turbine.javac.JavacTurbineCompileRequest.Prune; import com.google.devtools.build.java.turbine.javac.ZipOutputFileManager.OutputFileObject; import com.sun.tools.javac.util.Context; @@ -78,7 +79,7 @@ public static Result compile(TurbineOptions turbineOptions) throws IOException { } /** A header compilation result. */ - enum Result { + public enum Result { /** The compilation succeeded with the reduced classpath optimization. */ OK_WITH_REDUCED_CLASSPATH(true), @@ -94,11 +95,11 @@ private Result(boolean ok) { this.ok = ok; } - boolean ok() { + public boolean ok() { return ok; } - int exitCode() { + public int exitCode() { return ok ? 0 : 1; } } @@ -156,6 +157,11 @@ Result compile() throws IOException { .setBootClassPath(asPaths(turbineOptions.bootClassPath())) .setProcessorClassPath(processorpath); + if (!Collections.disjoint( + turbineOptions.processors(), turbineOptions.blacklistedProcessors())) { + requestBuilder.setPrune(Prune.NO); + } + StrictJavaDeps strictDepsMode = StrictJavaDeps.valueOf(turbineOptions.strictDepsMode()); DependencyModule dependencyModule = buildDependencyModule(turbineOptions, strictDepsMode); diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/javac/JavacTurbineCompileRequest.java b/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/javac/JavacTurbineCompileRequest.java index 68f99fe9d6dee5..284d2e2083ca3c 100644 --- a/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/javac/JavacTurbineCompileRequest.java +++ b/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/javac/JavacTurbineCompileRequest.java @@ -14,8 +14,11 @@ package com.google.devtools.build.java.turbine.javac; +import static com.google.common.base.Preconditions.checkNotNull; + import com.google.common.collect.ImmutableList; import com.google.devtools.build.buildjar.javac.plugins.dependency.StrictJavaDepsPlugin; +import com.google.devtools.build.java.turbine.javac.JavacTurbineCompileRequest.Prune; import java.nio.file.Path; @@ -24,23 +27,31 @@ /** The input to a {@link JavacTurbineCompiler} compilation. */ class JavacTurbineCompileRequest { + enum Prune { + YES, + NO + } + private final ImmutableList classPath; private final ImmutableList bootClassPath; private final ImmutableList processorClassPath; private final ImmutableList javacOptions; @Nullable private final StrictJavaDepsPlugin strictJavaDepsPlugin; + private final Prune prune; JavacTurbineCompileRequest( ImmutableList classPath, ImmutableList bootClassPath, ImmutableList processorClassPath, ImmutableList javacOptions, - @Nullable StrictJavaDepsPlugin strictJavaDepsPlugin) { - this.classPath = classPath; - this.bootClassPath = bootClassPath; - this.processorClassPath = processorClassPath; - this.javacOptions = javacOptions; + @Nullable StrictJavaDepsPlugin strictJavaDepsPlugin, + Prune prune) { + this.classPath = checkNotNull(classPath); + this.bootClassPath = checkNotNull(bootClassPath); + this.processorClassPath = checkNotNull(processorClassPath); + this.javacOptions = checkNotNull(javacOptions); this.strictJavaDepsPlugin = strictJavaDepsPlugin; + this.prune = checkNotNull(prune); } /** The class path; correspond's to javac -classpath. */ @@ -71,6 +82,11 @@ StrictJavaDepsPlugin strictJavaDepsPlugin() { return strictJavaDepsPlugin; } + /** Whether to perform a relaxed header-only compilation. */ + Prune prune() { + return prune; + } + static JavacTurbineCompileRequest.Builder builder() { return new Builder(); } @@ -81,12 +97,13 @@ static class Builder { private ImmutableList processorClassPath; private ImmutableList javacOptions; @Nullable private StrictJavaDepsPlugin strictDepsPlugin; + private Prune prune = Prune.YES; private Builder() {} JavacTurbineCompileRequest build() { return new JavacTurbineCompileRequest( - classPath, bootClassPath, processorClassPath, javacOptions, strictDepsPlugin); + classPath, bootClassPath, processorClassPath, javacOptions, strictDepsPlugin, prune); } Builder setClassPath(ImmutableList classPath) { @@ -113,5 +130,10 @@ Builder setStrictDepsPlugin(@Nullable StrictJavaDepsPlugin strictDepsPlugin) { this.strictDepsPlugin = strictDepsPlugin; return this; } + + Builder setPrune(Prune prune) { + this.prune = prune; + return this; + } } } diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/javac/JavacTurbineCompiler.java b/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/javac/JavacTurbineCompiler.java index 0a59323cc8f98e..0718ec5968b425 100644 --- a/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/javac/JavacTurbineCompiler.java +++ b/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/javac/JavacTurbineCompiler.java @@ -16,6 +16,7 @@ import com.google.common.collect.ImmutableMap; import com.google.devtools.build.buildjar.javac.plugins.dependency.StrictJavaDepsPlugin; +import com.google.devtools.build.java.turbine.javac.JavacTurbineCompileRequest.Prune; import com.google.devtools.build.java.turbine.javac.JavacTurbineCompileResult.Status; import com.google.devtools.build.java.turbine.javac.ZipOutputFileManager.OutputFileObject; @@ -50,7 +51,7 @@ static JavacTurbineCompileResult compile(JavacTurbineCompileRequest request) thr try (PrintWriter pw = new PrintWriter(sw)) { ZipOutputFileManager.preRegister(context, files); - setupContext(context, request.strictJavaDepsPlugin()); + setupContext(context, request.strictJavaDepsPlugin(), request.prune()); CacheFSInfo.preRegister(context); context.put(Log.outKey, pw); @@ -93,7 +94,7 @@ static JavacTurbineCompileResult compile(JavacTurbineCompileRequest request) thr return new JavacTurbineCompileResult(ImmutableMap.copyOf(files), status, sw, context); } - static void setupContext(Context context, @Nullable StrictJavaDepsPlugin sjd) { - JavacTurbineJavaCompiler.preRegister(context, sjd); + static void setupContext(Context context, @Nullable StrictJavaDepsPlugin sjd, Prune prune) { + JavacTurbineJavaCompiler.preRegister(context, sjd, prune); } } diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/javac/JavacTurbineJavaCompiler.java b/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/javac/JavacTurbineJavaCompiler.java index 874b1811a49930..d49863a4235cf2 100644 --- a/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/javac/JavacTurbineJavaCompiler.java +++ b/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/javac/JavacTurbineJavaCompiler.java @@ -15,6 +15,7 @@ package com.google.devtools.build.java.turbine.javac; import com.google.devtools.build.buildjar.javac.plugins.dependency.StrictJavaDepsPlugin; +import com.google.devtools.build.java.turbine.javac.JavacTurbineCompileRequest.Prune; import com.sun.tools.javac.comp.AttrContext; import com.sun.tools.javac.comp.CompileStates.CompileState; @@ -38,15 +39,21 @@ class JavacTurbineJavaCompiler extends JavaCompiler implements AutoCloseable { @Nullable private final StrictJavaDepsPlugin strictJavaDeps; + private final boolean prune; - public JavacTurbineJavaCompiler(Context context, @Nullable StrictJavaDepsPlugin strictJavaDeps) { + public JavacTurbineJavaCompiler( + Context context, @Nullable StrictJavaDepsPlugin strictJavaDeps, Prune prune) { super(context); this.strictJavaDeps = strictJavaDeps; + this.prune = prune == Prune.YES; } @Override protected JCCompilationUnit parse(JavaFileObject javaFileObject, CharSequence charSequence) { JCCompilationUnit result = super.parse(javaFileObject, charSequence); + if (!prune) { + return result; + } TreePruner.prune(result); return result; } @@ -65,6 +72,10 @@ public Env attribute(Env env) { @Override protected void flow(Env env, Queue> results) { + if (!prune) { + super.flow(env, results); + return; + } // skip FLOW (as if -relax was enabled, except -relax is broken for JDK >= 8) if (!compileStates.isDone(env, CompileState.FLOW)) { compileStates.put(env, CompileState.FLOW); @@ -83,13 +94,14 @@ public void close() { * Override the default {@link JavaCompiler} implementation with {@link JavacTurbineJavaCompiler} * for the given compilation context. */ - public static void preRegister(Context context, @Nullable final StrictJavaDepsPlugin sjd) { + public static void preRegister( + Context context, @Nullable final StrictJavaDepsPlugin sjd, final Prune prune) { context.put( compilerKey, new Context.Factory() { @Override public JavaCompiler make(Context c) { - return new JavacTurbineJavaCompiler(c, sjd); + return new JavacTurbineJavaCompiler(c, sjd, prune); } }); } diff --git a/src/java_tools/buildjar/javatests/com/google/devtools/build/java/turbine/javac/BUILD b/src/java_tools/buildjar/javatests/com/google/devtools/build/java/turbine/javac/BUILD index 04419b0ad56d1a..4132bba9df711b 100644 --- a/src/java_tools/buildjar/javatests/com/google/devtools/build/java/turbine/javac/BUILD +++ b/src/java_tools/buildjar/javatests/com/google/devtools/build/java/turbine/javac/BUILD @@ -1,6 +1,13 @@ java_test( name = "JavacTurbineTest", srcs = ["JavacTurbineTest.java"], + data = [ + "//third_party/java/jdk/langtools:javac_jar", + ], + jvm_flags = [ + # Simulates how Bazel invokes turbine + "-Xbootclasspath/p:$(location //third_party/java/jdk/langtools:javac_jar)", + ], tags = ["jdk8"], deps = [ "//src/java_tools/buildjar/java/com/google/devtools/build/java/turbine:turbine_options", diff --git a/src/java_tools/buildjar/javatests/com/google/devtools/build/java/turbine/javac/JavacTurbineTest.java b/src/java_tools/buildjar/javatests/com/google/devtools/build/java/turbine/javac/JavacTurbineTest.java index 2fdc22d9d63214..234f5d0865332e 100644 --- a/src/java_tools/buildjar/javatests/com/google/devtools/build/java/turbine/javac/JavacTurbineTest.java +++ b/src/java_tools/buildjar/javatests/com/google/devtools/build/java/turbine/javac/JavacTurbineTest.java @@ -15,6 +15,7 @@ package com.google.devtools.build.java.turbine.javac; import static com.google.common.truth.Truth.assertThat; +import static java.nio.charset.StandardCharsets.UTF_8; import com.google.common.base.Function; import com.google.common.base.Joiner; @@ -28,7 +29,12 @@ import com.google.devtools.build.lib.view.proto.Deps; import com.google.devtools.build.lib.view.proto.Deps.Dependency; +import com.sun.source.tree.LiteralTree; import com.sun.source.util.JavacTask; +import com.sun.source.util.TaskEvent; +import com.sun.source.util.TaskEvent.Kind; +import com.sun.source.util.TaskListener; +import com.sun.source.util.TreeScanner; import com.sun.tools.javac.api.ClientCodeWrapper.Trusted; import com.sun.tools.javac.api.JavacTool; import com.sun.tools.javac.file.JavacFileManager; @@ -52,7 +58,6 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.net.URI; -import java.nio.charset.StandardCharsets; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; @@ -63,6 +68,7 @@ import java.util.Collections; import java.util.Enumeration; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -71,6 +77,7 @@ import java.util.jar.JarOutputStream; import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.SourceVersion; @@ -120,7 +127,7 @@ public void setUp() throws IOException { private void addSourceLines(String path, String... lines) throws IOException { Path source = sourcedir.resolve(path); sources.add(source); - Files.write(source, Arrays.asList(lines), StandardCharsets.UTF_8); + Files.write(source, Arrays.asList(lines), UTF_8); } void compile() throws IOException { @@ -231,7 +238,7 @@ public boolean process(Set annotations, RoundEnvironment try { JavaFileObject sourceFile = processingEnv.getFiler().createSourceFile("Generated", element); try (OutputStream os = sourceFile.openOutputStream()) { - os.write("public class Generated {}".getBytes(StandardCharsets.UTF_8)); + os.write("public class Generated {}".getBytes(UTF_8)); } } catch (IOException e) { throw new IOError(e); @@ -242,7 +249,7 @@ public boolean process(Set annotations, RoundEnvironment .getFiler() .createResource(StandardLocation.CLASS_OUTPUT, "com.foo", "hello.txt", element); try (OutputStream os = file.openOutputStream()) { - os.write("hello".getBytes(StandardCharsets.UTF_8)); + os.write("hello".getBytes(UTF_8)); } } catch (IOException e) { throw new IOError(e); @@ -455,7 +462,7 @@ private void compileLib( Path jar, Iterable classpath, Iterable units) throws IOException { final Path outdir = temp.newFolder().toPath(); - JavacFileManager fm = new JavacFileManager(new Context(), false, StandardCharsets.UTF_8); + JavacFileManager fm = new JavacFileManager(new Context(), false, UTF_8); fm.setLocationFromPaths(StandardLocation.CLASS_OUTPUT, Collections.singleton(outdir)); fm.setLocationFromPaths(StandardLocation.CLASS_PATH, classpath); List options = Arrays.asList("-d", outdir.toString()); @@ -795,10 +802,9 @@ public boolean process(Set annotations, RoundEnvironment try { JavaFileObject sourceFile = processingEnv.getFiler().createSourceFile("Generated", element); try (OutputStream os = sourceFile.openOutputStream()) { - os.write( - "class Generated { public static String x = \"".getBytes(StandardCharsets.UTF_8)); + os.write("class Generated { public static String x = \"".getBytes(UTF_8)); os.write(0xc2); // write an unpaired surrogate - os.write("\";}}".getBytes(StandardCharsets.UTF_8)); + os.write("\";}}".getBytes(UTF_8)); } } catch (IOException e) { throw new IOError(e); @@ -936,7 +942,7 @@ public boolean process(Set annotations, RoundEnvironment .getFiler() .createResource(StandardLocation.CLASS_OUTPUT, "", "result.txt"); try (OutputStream os = fileObject.openOutputStream()) { - os.write(message.getBytes(StandardCharsets.UTF_8)); + os.write(message.getBytes(UTF_8)); } } catch (IOException e) { throw new IOError(e); @@ -951,15 +957,7 @@ public void maskProcessorClasspath() throws Exception { addSourceLines("Hello.java", "@MyAnnotation class Hello {}"); // create a jar containing only HostClasspathProcessor - Path processorJar = temp.newFile("libprocessor.jar").toPath(); - try (OutputStream os = Files.newOutputStream(processorJar); - JarOutputStream jos = new JarOutputStream(os)) { - String classFileName = HostClasspathProcessor.class.getName().replace('.', '/') + ".class"; - jos.putNextEntry(new JarEntry(classFileName)); - try (InputStream is = getClass().getClassLoader().getResourceAsStream(classFileName)) { - ByteStreams.copy(is, jos); - } - } + Path processorJar = createClassJar("libprocessor.jar", HostClasspathProcessor.class); optionsBuilder.addProcessors(ImmutableList.of(HostClasspathProcessor.class.getName())); optionsBuilder.addProcessorPathEntries(ImmutableList.of(processorJar.toString())); @@ -970,27 +968,42 @@ public void maskProcessorClasspath() throws Exception { Map outputs = collectOutputs(); assertThat(outputs.keySet()).contains("result.txt"); - String text = new String(outputs.get("result.txt"), StandardCharsets.UTF_8); + String text = new String(outputs.get("result.txt"), UTF_8); assertThat(text) .contains( "java.lang.NoClassDefFoundError:" + " com/google/devtools/build/java/turbine/javac/JavacTurbine"); } + private Path createClassJar(String jarName, Class... classes) throws IOException { + Path jarPath = temp.newFile(jarName).toPath(); + try (OutputStream os = Files.newOutputStream(jarPath); + JarOutputStream jos = new JarOutputStream(os)) { + for (Class clazz : classes) { + String classFileName = clazz.getName().replace('.', '/') + ".class"; + jos.putNextEntry(new JarEntry(classFileName)); + try (InputStream is = getClass().getClassLoader().getResourceAsStream(classFileName)) { + ByteStreams.copy(is, jos); + } + } + } + return jarPath; + } + @Test public void overlappingSourceJars() throws Exception { Path sourceJar1 = temp.newFile("srcs1.jar").toPath(); try (OutputStream os = Files.newOutputStream(sourceJar1); JarOutputStream jos = new JarOutputStream(os)) { jos.putNextEntry(new JarEntry("Hello.java")); - jos.write("public class Hello {}".getBytes(StandardCharsets.UTF_8)); + jos.write("public class Hello {}".getBytes(UTF_8)); } Path sourceJar2 = temp.newFile("srcs2.jar").toPath(); try (OutputStream os = Files.newOutputStream(sourceJar2); JarOutputStream jos = new JarOutputStream(os)) { jos.putNextEntry(new JarEntry("Hello.java")); - jos.write("public class Hello {}".getBytes(StandardCharsets.UTF_8)); + jos.write("public class Hello {}".getBytes(UTF_8)); } optionsBuilder.setSourceJars(ImmutableList.of(sourceJar2.toString(), sourceJar1.toString())); @@ -1090,4 +1103,110 @@ public void emptySources() throws Exception { Map outputs = collectOutputs(); assertThat(outputs.keySet()).containsExactly("dummy"); } + + /** An annotation processor that violates the contract. */ + @SupportedAnnotationTypes("*") + public static class MisguidedAnnotationProcessor extends AbstractProcessor { + + public final class Scanner extends TreeScanner { + @Override + public Void visitLiteral(LiteralTree tree, Void unused) { + values.add(tree.getValue()); + return null; + } + } + + public final class Listener implements TaskListener { + + public final ProcessingEnvironment processingEnv; + + Listener(ProcessingEnvironment processingEnv) { + this.processingEnv = processingEnv; + } + + @Override + public void started(TaskEvent e) {} + + @Override + public void finished(TaskEvent e) { + if (e.getKind() == Kind.ANALYZE) { + e.getCompilationUnit().accept(new Scanner(), null); + } else if (e.getKind() == Kind.GENERATE) { + try { + FileObject file = + processingEnv + .getFiler() + .createResource( + StandardLocation.CLASS_OUTPUT, "", "output.txt", e.getTypeElement()); + try (OutputStream os = file.openOutputStream()) { + os.write(values.toString().getBytes(UTF_8)); + } + } catch (IOException exception) { + throw new IOError(exception); + } + } + } + } + + public final Set values = new LinkedHashSet<>(); + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latest(); + } + + @Override + public synchronized void init(final ProcessingEnvironment processingEnv) { + JavacTask.instance(processingEnv).addTaskListener(new Listener(processingEnv)); + } + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + return false; + } + } + + void setupMisguidedProcessor() throws Exception { + addSourceLines( + "Hello.java", + "@Deprecated class Hello {", + " int x = 42;", + " String s = \"hello\";", + " double y = 42.1;", + "}"); + + Path processorJar = + createClassJar( + "libprocessor.jar", + MisguidedAnnotationProcessor.class, + MisguidedAnnotationProcessor.Listener.class, + MisguidedAnnotationProcessor.Scanner.class); + + optionsBuilder.addProcessors(ImmutableList.of(MisguidedAnnotationProcessor.class.getName())); + optionsBuilder.addProcessorPathEntries(ImmutableList.of(processorJar.toString())); + } + + @Test + public void misguidedProcessor_pruning() throws Exception { + setupMisguidedProcessor(); + compile(); + Map outputs = collectOutputs(); + + assertThat(outputs.keySet()).containsExactly("Hello.class", "output.txt"); + String output = new String(outputs.get("output.txt"), UTF_8); + assertThat(output).isEqualTo("[]"); + } + + @Test + public void misguidedProcessor() throws Exception { + setupMisguidedProcessor(); + optionsBuilder.addBlacklistedProcessors( + ImmutableList.of(MisguidedAnnotationProcessor.class.getName())); + compile(); + Map outputs = collectOutputs(); + + assertThat(outputs.keySet()).containsExactly("Hello.class", "output.txt"); + String output = new String(outputs.get("output.txt"), UTF_8); + assertThat(output).isEqualTo("[42, hello, 42.1]"); + } }