# HG changeset patch # User jjg # Date 1353353929 28800 # Node ID aa687b312c97174a861d113d4030d352d6628895 # Parent 86d8d242b0c45bec97640ec1169fe8edd27b5891 8001098: Provide a simple light-weight "plug-in" mechanism for javac Reviewed-by: mcimadamore diff -r 86d8d242b0c4 -r aa687b312c97 langtools/src/share/classes/com/sun/source/util/Plugin.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/src/share/classes/com/sun/source/util/Plugin.java Mon Nov 19 11:38:49 2012 -0800 @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.source.util; + +import java.util.ServiceLoader; +import javax.tools.StandardLocation; + +/** + * The interface for a javac plug-in. + * + *

The javac plug-in mechanism allows a user to specify one or more plug-ins + * on the javac command line, to be started soon after the compilation + * has begun. Plug-ins are identified by a user-friendly name. Each plug-in that + * is started will be passed an array of strings, which may be used to + * provide the plug-in with values for any desired options or other arguments. + * + *

Plug-ins are located via a {@link ServiceLoader}, + * using the same class path as annotation processors (i.e. + * {@link StandardLocation#PROCESSOR_PATH PROCESSOR_PATH} or + * {@code -processorpath}). + * + *

It is expected that a typical plug-in will simply register a + * {@link TaskListener} to be informed of events during the execution + * of the compilation, and that the rest of the work will be done + * by the task listener. + * + * @since 1.8 + */ +public interface Plugin { + /** + * Get the user-friendly name of this plug-in. + * @return the user-friendly name of the plug-in + */ + String getName(); + + /** + * Invoke the plug-in for a given compilation task. + * @param task The compilation task that has just been started + * @param args Arguments, if any, for the plug-in + */ + void call(JavacTask task, String... args); +} diff -r 86d8d242b0c4 -r aa687b312c97 langtools/src/share/classes/com/sun/source/util/Trees.java --- a/langtools/src/share/classes/com/sun/source/util/Trees.java Sat Nov 17 19:01:03 2012 +0000 +++ b/langtools/src/share/classes/com/sun/source/util/Trees.java Mon Nov 19 11:38:49 2012 -0800 @@ -58,7 +58,9 @@ * @throws IllegalArgumentException if the task does not support the Trees API. */ public static Trees instance(CompilationTask task) { - if (!task.getClass().getName().equals("com.sun.tools.javac.api.JavacTaskImpl")) + String taskClassName = task.getClass().getName(); + if (!taskClassName.equals("com.sun.tools.javac.api.JavacTaskImpl") + && !taskClassName.equals("com.sun.tools.javac.api.BasicJavacTask")) throw new IllegalArgumentException(); return getJavacTrees(CompilationTask.class, task); } diff -r 86d8d242b0c4 -r aa687b312c97 langtools/src/share/classes/com/sun/tools/javac/api/BasicJavacTask.java --- a/langtools/src/share/classes/com/sun/tools/javac/api/BasicJavacTask.java Sat Nov 17 19:01:03 2012 +0000 +++ b/langtools/src/share/classes/com/sun/tools/javac/api/BasicJavacTask.java Mon Nov 19 11:38:49 2012 -0800 @@ -140,6 +140,14 @@ * For internal use only. This method will be * removed without warning. */ + public Context getContext() { + return context; + } + + /** + * For internal use only. This method will be + * removed without warning. + */ public void updateContext(Context newContext) { context = newContext; } diff -r 86d8d242b0c4 -r aa687b312c97 langtools/src/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java --- a/langtools/src/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java Sat Nov 17 19:01:03 2012 +0000 +++ b/langtools/src/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java Mon Nov 19 11:38:49 2012 -0800 @@ -489,22 +489,6 @@ * For internal use only. This method will be * removed without warning. */ - public Context getContext() { - return context; - } - - /** - * For internal use only. This method will be - * removed without warning. - */ - public void updateContext(Context newContext) { - context = newContext; - } - - /** - * For internal use only. This method will be - * removed without warning. - */ public Type parseType(String expr, TypeElement scope) { if (expr == null || expr.equals("")) throw new IllegalArgumentException(); diff -r 86d8d242b0c4 -r aa687b312c97 langtools/src/share/classes/com/sun/tools/javac/api/JavacTrees.java --- a/langtools/src/share/classes/com/sun/tools/javac/api/JavacTrees.java Sat Nov 17 19:01:03 2012 +0000 +++ b/langtools/src/share/classes/com/sun/tools/javac/api/JavacTrees.java Mon Nov 19 11:38:49 2012 -0800 @@ -121,9 +121,9 @@ // called reflectively from Trees.instance(CompilationTask task) public static JavacTrees instance(JavaCompiler.CompilationTask task) { - if (!(task instanceof JavacTaskImpl)) + if (!(task instanceof BasicJavacTask)) throw new IllegalArgumentException(); - return instance(((JavacTaskImpl)task).getContext()); + return instance(((BasicJavacTask)task).getContext()); } // called reflectively from Trees.instance(ProcessingEnvironment env) diff -r 86d8d242b0c4 -r aa687b312c97 langtools/src/share/classes/com/sun/tools/javac/main/JavaCompiler.java --- a/langtools/src/share/classes/com/sun/tools/javac/main/JavaCompiler.java Sat Nov 17 19:01:03 2012 +0000 +++ b/langtools/src/share/classes/com/sun/tools/javac/main/JavaCompiler.java Mon Nov 19 11:38:49 2012 -0800 @@ -1040,7 +1040,8 @@ if (options.isSet(PROC, "none")) { processAnnotations = false; } else if (procEnvImpl == null) { - procEnvImpl = new JavacProcessingEnvironment(context, processors); + procEnvImpl = JavacProcessingEnvironment.instance(context); + procEnvImpl.setProcessors(processors); processAnnotations = procEnvImpl.atLeastOneProcessor(); if (processAnnotations) { diff -r 86d8d242b0c4 -r aa687b312c97 langtools/src/share/classes/com/sun/tools/javac/main/Main.java --- a/langtools/src/share/classes/com/sun/tools/javac/main/Main.java Sat Nov 17 19:01:03 2012 +0000 +++ b/langtools/src/share/classes/com/sun/tools/javac/main/Main.java Mon Nov 19 11:38:49 2012 -0800 @@ -33,24 +33,29 @@ import java.security.MessageDigest; import java.util.Arrays; import java.util.Collection; +import java.util.Iterator; import java.util.LinkedHashSet; +import java.util.ServiceLoader; import java.util.Set; + +import javax.annotation.processing.Processor; import javax.tools.JavaFileManager; import javax.tools.JavaFileObject; -import javax.annotation.processing.Processor; +import com.sun.source.util.JavacTask; +import com.sun.source.util.Plugin; import com.sun.tools.javac.code.Source; import com.sun.tools.javac.file.CacheFSInfo; import com.sun.tools.javac.file.JavacFileManager; import com.sun.tools.javac.jvm.Target; +import com.sun.tools.javac.processing.AnnotationProcessingError; +import com.sun.tools.javac.processing.JavacProcessingEnvironment; import com.sun.tools.javac.util.*; -import com.sun.tools.javac.util.Log.WriterKind; import com.sun.tools.javac.util.Log.PrefixKind; -import com.sun.tools.javac.processing.AnnotationProcessingError; - +import com.sun.tools.javac.util.Log.WriterKind; import static com.sun.tools.javac.main.Option.*; -/** This class provides a commandline interface to the GJC compiler. +/** This class provides a command line interface to the javac compiler. * *

This is NOT part of any supported API. * If you write code that depends on this, you do so at your own risk. @@ -423,6 +428,42 @@ if (batchMode) CacheFSInfo.preRegister(context); + // invoke any available plugins + String plugins = options.get(PLUGIN); + if (plugins != null) { + JavacProcessingEnvironment pEnv = JavacProcessingEnvironment.instance(context); + ClassLoader cl = pEnv.getProcessorClassLoader(); + ServiceLoader sl = ServiceLoader.load(Plugin.class, cl); + Set> pluginsToCall = new LinkedHashSet>(); + for (String plugin: plugins.split("\\x00")) { + pluginsToCall.add(List.from(plugin.split("\\s+"))); + } + JavacTask task = null; + Iterator iter = sl.iterator(); + while (iter.hasNext()) { + Plugin plugin = iter.next(); + for (List p: pluginsToCall) { + if (plugin.getName().equals(p.head)) { + pluginsToCall.remove(p); + try { + if (task == null) + task = JavacTask.instance(pEnv); + plugin.call(task, p.tail.toArray(new String[p.tail.size()])); + } catch (Throwable ex) { + if (apiMode) + throw new RuntimeException(ex); + pluginMessage(ex); + return Result.SYSERR; + } + + } + } + } + for (List p: pluginsToCall) { + log.printLines(PrefixKind.JAVAC, "msg.plugin.not.found", p.head); + } + } + fileManager = context.get(JavaFileManager.class); comp = JavaCompiler.instance(context); @@ -537,6 +578,14 @@ ex.getCause().printStackTrace(log.getWriter(WriterKind.NOTICE)); } + /** Print a message reporting an uncaught exception from an + * annotation processor. + */ + void pluginMessage(Throwable ex) { + log.printLines(PrefixKind.JAVAC, "msg.plugin.uncaught.exception"); + ex.printStackTrace(log.getWriter(WriterKind.NOTICE)); + } + /** Display the location and checksum of a class. */ void showClass(String className) { PrintWriter pw = log.getWriter(WriterKind.NOTICE); diff -r 86d8d242b0c4 -r aa687b312c97 langtools/src/share/classes/com/sun/tools/javac/main/Option.java --- a/langtools/src/share/classes/com/sun/tools/javac/main/Option.java Sat Nov 17 19:01:03 2012 +0000 +++ b/langtools/src/share/classes/com/sun/tools/javac/main/Option.java Mon Nov 19 11:38:49 2012 -0800 @@ -393,6 +393,16 @@ /* -Xjcov produces tables to support the code coverage tool jcov. */ XJCOV("-Xjcov", null, HIDDEN, BASIC), + PLUGIN("-Xplugin:", "opt.arg.plugin", "opt.plugin", EXTENDED, BASIC) { + @Override + public boolean process(OptionHelper helper, String option) { + String p = option.substring(option.indexOf(':') + 1); + String prev = helper.get(PLUGIN); + helper.put(PLUGIN.text, (prev == null) ? p : prev + '\0' + p.trim()); + return false; + } + }, + /* This is a back door to the compiler's option table. * -XDx=y sets the option x to the value y. * -XDx sets the option x to the value x. diff -r 86d8d242b0c4 -r aa687b312c97 langtools/src/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java --- a/langtools/src/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java Sat Nov 17 19:01:03 2012 +0000 +++ b/langtools/src/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java Mon Nov 19 11:38:49 2012 -0800 @@ -145,6 +145,7 @@ Source source; private ClassLoader processorClassLoader; + private SecurityException processorClassLoaderException; /** * JavacMessages object used for localization @@ -155,7 +156,15 @@ private Context context; - public JavacProcessingEnvironment(Context context, Iterable processors) { + /** Get the JavacProcessingEnvironment instance for this context. */ + public static JavacProcessingEnvironment instance(Context context) { + JavacProcessingEnvironment instance = context.get(JavacProcessingEnvironment.class); + if (instance == null) + instance = new JavacProcessingEnvironment(context); + return instance; + } + + protected JavacProcessingEnvironment(Context context) { this.context = context; log = Log.instance(context); source = Source.instance(context); @@ -184,6 +193,11 @@ unmatchedProcessorOptions = initUnmatchedProcessorOptions(); messages = JavacMessages.instance(context); taskListener = MultiTaskListener.instance(context); + initProcessorClassLoader(); + } + + public void setProcessors(Iterable processors) { + Assert.checkNull(discoveredProcs); initProcessorIterator(context, processors); } @@ -199,6 +213,23 @@ return Collections.unmodifiableSet(platformAnnotations); } + private void initProcessorClassLoader() { + JavaFileManager fileManager = context.get(JavaFileManager.class); + try { + // If processorpath is not explicitly set, use the classpath. + processorClassLoader = fileManager.hasLocation(ANNOTATION_PROCESSOR_PATH) + ? fileManager.getClassLoader(ANNOTATION_PROCESSOR_PATH) + : fileManager.getClassLoader(CLASS_PATH); + + if (processorClassLoader != null && processorClassLoader instanceof Closeable) { + JavaCompiler compiler = JavaCompiler.instance(context); + compiler.closeables = compiler.closeables.prepend((Closeable) processorClassLoader); + } + } catch (SecurityException e) { + processorClassLoaderException = e; + } + } + private void initProcessorIterator(Context context, Iterable processors) { Log log = Log.instance(context); Iterator processorIterator; @@ -217,18 +248,7 @@ processorIterator = processors.iterator(); } else { String processorNames = options.get(PROCESSOR); - JavaFileManager fileManager = context.get(JavaFileManager.class); - try { - // If processorpath is not explicitly set, use the classpath. - processorClassLoader = fileManager.hasLocation(ANNOTATION_PROCESSOR_PATH) - ? fileManager.getClassLoader(ANNOTATION_PROCESSOR_PATH) - : fileManager.getClassLoader(CLASS_PATH); - - if (processorClassLoader != null && processorClassLoader instanceof Closeable) { - JavaCompiler compiler = JavaCompiler.instance(context); - compiler.closeables = compiler.closeables.prepend((Closeable) processorClassLoader); - } - + if (processorClassLoaderException == null) { /* * If the "-processor" option is used, search the appropriate * path for the named class. Otherwise, use a service @@ -239,14 +259,15 @@ } else { processorIterator = new ServiceIterator(processorClassLoader, log); } - } catch (SecurityException e) { + } else { /* * A security exception will occur if we can't create a classloader. * Ignore the exception if, with hindsight, we didn't need it anyway * (i.e. no processor was specified either explicitly, or implicitly, * in service configuration file.) Otherwise, we cannot continue. */ - processorIterator = handleServiceLoaderUnavailability("proc.cant.create.loader", e); + processorIterator = handleServiceLoaderUnavailability("proc.cant.create.loader", + processorClassLoaderException); } } discoveredProcs = new DiscoveredProcessors(processorIterator); @@ -1473,13 +1494,19 @@ } /** - * For internal use only. This method will be - * removed without warning. + * For internal use only. This method may be removed without warning. */ public Context getContext() { return context; } + /** + * For internal use only. This method may be removed without warning. + */ + public ClassLoader getProcessorClassLoader() { + return processorClassLoader; + } + public String toString() { return "javac ProcessingEnvironment"; } diff -r 86d8d242b0c4 -r aa687b312c97 langtools/src/share/classes/com/sun/tools/javac/resources/javac.properties --- a/langtools/src/share/classes/com/sun/tools/javac/resources/javac.properties Sat Nov 17 19:01:03 2012 +0000 +++ b/langtools/src/share/classes/com/sun/tools/javac/resources/javac.properties Mon Nov 19 11:38:49 2012 -0800 @@ -99,6 +99,10 @@ javac.opt.arg.number=\ +javac.opt.plugin=\ + Name and optional arguments for a plug-in to be run +javac.opt.arg.plugin=\ + "name args" ## extended options @@ -185,6 +189,8 @@ not a directory: {0} javac.err.file.not.file=\ not a file: {0} +javac.msg.plugin.not.found=\ + plug-in not found: {0} ## messages javac.msg.usage.header=\ @@ -212,6 +218,10 @@ \n\nAn annotation processor threw an uncaught exception.\n\ Consult the following stack trace for details.\n +javac.msg.plugin.uncaught.exception=\ +\n\nA plugin threw an uncaught exception.\n\ +Consult the following stack trace for details.\n + javac.msg.resource=\ \n\nThe system is out of resources.\n\ Consult the following stack trace for details.\n diff -r 86d8d242b0c4 -r aa687b312c97 langtools/test/tools/javac/plugin/showtype/Identifiers.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javac/plugin/showtype/Identifiers.java Mon Nov 19 11:38:49 2012 -0800 @@ -0,0 +1,7 @@ +/* /nodynamiccopyright */ + +public class Identifiers { + public double E = Math.E; + public double PI = Math.PI; + public double PIE = PI + E; +} diff -r 86d8d242b0c4 -r aa687b312c97 langtools/test/tools/javac/plugin/showtype/Identifiers.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javac/plugin/showtype/Identifiers.out Mon Nov 19 11:38:49 2012 -0800 @@ -0,0 +1,21 @@ +Identifiers.java:3: Note: type is ()void +public class Identifiers { + ^ +Identifiers.java:4: Note: type is double + public double E = Math.E; + ^ +Identifiers.java:4: Note: type is java.lang.Math + public double E = Math.E; + ^ +Identifiers.java:5: Note: type is double + public double PI = Math.PI; + ^ +Identifiers.java:5: Note: type is java.lang.Math + public double PI = Math.PI; + ^ +Identifiers.java:6: Note: type is double + public double PIE = PI + E; + ^ +Identifiers.java:6: Note: type is double + public double PIE = PI + E; + ^ diff -r 86d8d242b0c4 -r aa687b312c97 langtools/test/tools/javac/plugin/showtype/Identifiers_PI.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javac/plugin/showtype/Identifiers_PI.out Mon Nov 19 11:38:49 2012 -0800 @@ -0,0 +1,6 @@ +Identifiers.java:5: Note: type is double + public double PI = Math.PI; + ^ +Identifiers.java:6: Note: type is double + public double PIE = PI + E; + ^ diff -r 86d8d242b0c4 -r aa687b312c97 langtools/test/tools/javac/plugin/showtype/ShowTypePlugin.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javac/plugin/showtype/ShowTypePlugin.java Mon Nov 19 11:38:49 2012 -0800 @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import com.sun.source.tree.CompilationUnitTree; +import com.sun.source.tree.IdentifierTree; +import com.sun.source.tree.MemberSelectTree; +import com.sun.source.tree.Tree; +import com.sun.source.util.JavacTask; +import com.sun.source.util.Plugin; +import com.sun.source.util.TaskEvent; +import com.sun.source.util.TaskListener; +import com.sun.source.util.TreePathScanner; +import com.sun.source.util.Trees; +import java.util.regex.Pattern; +import javax.lang.model.type.TypeMirror; +import javax.tools.Diagnostic.Kind; + +public class ShowTypePlugin implements Plugin { + + public String getName() { + return "showtype"; + } + + public void call(JavacTask task, String... args) { + Pattern pattern = null; + if (args.length == 1) + pattern = Pattern.compile(args[0]); + task.addTaskListener(new PostAnalyzeTaskListener(task, pattern)); + } + + private static class PostAnalyzeTaskListener implements TaskListener { + private final ShowTypeTreeVisitor visitor; + + PostAnalyzeTaskListener(JavacTask task, Pattern pattern) { + visitor = new ShowTypeTreeVisitor(task, pattern); + } + + @Override + public void started(TaskEvent taskEvent) { } + + @Override + public void finished(TaskEvent taskEvent) { + if (taskEvent.getKind().equals(TaskEvent.Kind.ANALYZE)) { + CompilationUnitTree compilationUnit = taskEvent.getCompilationUnit(); + visitor.scan(compilationUnit, null); + } + } + } + + private static class ShowTypeTreeVisitor extends TreePathScanner { + private final Trees trees; + private final Pattern pattern; + private CompilationUnitTree currCompUnit; + + ShowTypeTreeVisitor(JavacTask task, Pattern pattern) { + trees = Trees.instance(task); + this.pattern = pattern; + } + + @Override + public Void visitCompilationUnit(CompilationUnitTree tree, Void ignore) { + currCompUnit = tree; + return super.visitCompilationUnit(tree, ignore); + } + + @Override + public Void visitIdentifier(IdentifierTree tree, Void ignore) { + show(tree, tree.getName()); + return super.visitIdentifier(tree, ignore); + } + + @Override + public Void visitMemberSelect(MemberSelectTree tree, Void ignore) { + show(tree, tree.getIdentifier()); + return super.visitMemberSelect(tree, ignore); + } + + void show(Tree tree, CharSequence name) { + if (pattern == null || pattern.matcher(name).matches()) { + TypeMirror type = trees.getTypeMirror(getCurrentPath()); + trees.printMessage(Kind.NOTE, "type is " + type, tree, currCompUnit); + } + } + } + +} diff -r 86d8d242b0c4 -r aa687b312c97 langtools/test/tools/javac/plugin/showtype/Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javac/plugin/showtype/Test.java Mon Nov 19 11:38:49 2012 -0800 @@ -0,0 +1,171 @@ + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileManager; +import javax.tools.JavaFileObject; +import javax.tools.StandardJavaFileManager; +import javax.tools.StandardLocation; +import javax.tools.ToolProvider; + +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8001098 + * @summary Provide a simple light-weight "plug-in" mechanism for javac + */ + +public class Test { + public static void main(String... args) throws Exception { + new Test().run(); + } + + final File testSrc; + final File pluginSrc; + final File pluginClasses ; + final File pluginJar; + final List ref1; + final List ref2; + final JavaCompiler compiler; + final StandardJavaFileManager fm; + + Test() throws Exception { + testSrc = new File(System.getProperty("test.src")); + pluginSrc = new File(testSrc, "ShowTypePlugin.java"); + pluginClasses = new File("plugin"); + pluginJar = new File("plugin.jar"); + ref1 = readFile(testSrc, "Identifiers.out"); + ref2 = readFile(testSrc, "Identifiers_PI.out"); + compiler = ToolProvider.getSystemJavaCompiler(); + fm = compiler.getStandardFileManager(null, null, null); + } + + void run() throws Exception { + // compile the plugin explicitly, to a non-standard directory + // so that we don't find it on the wrong path by accident + pluginClasses.mkdirs(); + compile("-d", pluginClasses.getPath(), pluginSrc.getPath()); + writeFile(new File(pluginClasses, "META-INF/services/com.sun.source.util.Plugin"), + "ShowTypePlugin\n"); + jar("cf", pluginJar.getPath(), "-C", pluginClasses.getPath(), "."); + + testCommandLine("-Xplugin:showtype", ref1); + testCommandLine("-Xplugin:showtype PI", ref2); + testAPI("-Xplugin:showtype", ref1); + testAPI("-Xplugin:showtype PI", ref2); + + if (errors > 0) + throw new Exception(errors + " errors occurred"); + } + + void testAPI(String opt, List ref) throws Exception { + File identifiers = new File(testSrc, "Identifiers.java"); + fm.setLocation(StandardLocation.ANNOTATION_PROCESSOR_PATH, Arrays.asList(pluginJar)); + fm.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(new File("."))); + List options = Arrays.asList(opt); + Iterable files = fm.getJavaFileObjects(identifiers); + + System.err.println("test api: " + options + " " + files); + + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + boolean ok = compiler.getTask(pw, fm, null, options, null, files).call(); + String out = sw.toString(); + System.err.println(out); + if (!ok) + error("testCommandLine: compilation failed"); + checkOutput(out, ref); + } + + void testCommandLine(String opt, List ref) { + File identifiers = new File(testSrc, "Identifiers.java"); + String[] args = { + "-d", ".", + "-processorpath", pluginJar.getPath(), + opt, + identifiers.getPath() }; + + System.err.println("test command line: " + Arrays.asList(args)); + + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + int rc = com.sun.tools.javac.Main.compile(args, pw); + String out = sw.toString(); + System.err.println(out); + if (rc != 0) + error("testCommandLine: compilation failed"); + checkOutput(out, ref); + } + + private void checkOutput(String out, List ref) { + List lines = Arrays.asList(out + .replaceAll(".*?([A-Za-z.]+:[0-9]+: .*)", "$1") // remove file directory + .split("[\r\n]+")); // allow for newline formats + if (!lines.equals(ref)) { + error("unexpected output"); + } + } + + private void compile(String... args) throws Exception { + System.err.println("compile: " + Arrays.asList(args)); + int rc = com.sun.tools.javac.Main.compile(args); + if (rc != 0) + throw new Exception("compiled failed, rc=" + rc); + } + + private void jar(String... args) throws Exception { + System.err.println("jar: " + Arrays.asList(args)); + boolean ok = new sun.tools.jar.Main(System.out, System.err, "jar").run(args); + if (!ok) + throw new Exception("jar failed"); + } + + private List readFile(File dir, String name) throws IOException { + return Files.readAllLines(new File(dir, name).toPath(), Charset.defaultCharset()); + } + + private void writeFile(File f, String body) throws IOException { + f.getParentFile().mkdirs(); + try (FileWriter out = new FileWriter(f)) { + out.write(body); + } + } + + private void error(String msg) { + System.err.println(msg); + errors++; + } + + int errors; +}