aboutsummaryrefslogtreecommitdiff
path: root/src/share/classes/sun/rmi/rmic/newrmic/Main.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/share/classes/sun/rmi/rmic/newrmic/Main.java')
-rw-r--r--src/share/classes/sun/rmi/rmic/newrmic/Main.java689
1 files changed, 689 insertions, 0 deletions
diff --git a/src/share/classes/sun/rmi/rmic/newrmic/Main.java b/src/share/classes/sun/rmi/rmic/newrmic/Main.java
new file mode 100644
index 000000000..e7dc7c899
--- /dev/null
+++ b/src/share/classes/sun/rmi/rmic/newrmic/Main.java
@@ -0,0 +1,689 @@
+/*
+ * Copyright 2003-2005 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.rmi.rmic.newrmic;
+
+import com.sun.javadoc.ClassDoc;
+import com.sun.javadoc.RootDoc;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import sun.rmi.rmic.newrmic.jrmp.JrmpGenerator;
+import sun.tools.util.CommandLine;
+
+/**
+ * The rmic front end. This class contains the "main" method for rmic
+ * command line invocation.
+ *
+ * A Main instance contains the stream to output error messages and
+ * other diagnostics to.
+ *
+ * An rmic compilation batch (for example, one rmic command line
+ * invocation) is executed by invoking the "compile" method of a Main
+ * instance.
+ *
+ * WARNING: The contents of this source file are not part of any
+ * supported API. Code that depends on them does so at its own risk:
+ * they are subject to change or removal without notice.
+ *
+ * NOTE: If and when there is a J2SE API for invoking SDK tools, this
+ * class should be updated to support that API.
+ *
+ * NOTE: This class is the front end for a "new" rmic implementation,
+ * which uses javadoc and the doclet API for reading class files and
+ * javac for compiling generated source files. This implementation is
+ * incomplete: it lacks any CORBA-based back end implementations, and
+ * thus the command line options "-idl", "-iiop", and their related
+ * options are not yet supported. The front end for the "old",
+ * oldjavac-based rmic implementation is sun.rmi.rmic.Main.
+ *
+ * @author Peter Jones
+ **/
+public class Main {
+
+ /*
+ * Implementation note:
+ *
+ * In order to use the doclet API to read class files, much of
+ * this implementation of rmic executes as a doclet within an
+ * invocation of javadoc. This class is used as the doclet class
+ * for such javadoc invocations, via its static "start" and
+ * "optionLength" methods. There is one javadoc invocation per
+ * rmic compilation batch.
+ *
+ * The only guaranteed way to pass data to a doclet through a
+ * javadoc invocation is through doclet-specific options on the
+ * javadoc "command line". Rather than passing numerous pieces of
+ * individual data in string form as javadoc options, we use a
+ * single doclet-specific option ("-batchID") to pass a numeric
+ * identifier that uniquely identifies the rmic compilation batch
+ * that the javadoc invocation is for, and that identifier can
+ * then be used as a key in a global table to retrieve an object
+ * containing all of batch-specific data (rmic command line
+ * arguments, etc.).
+ */
+
+ /** guards "batchCount" */
+ private static final Object batchCountLock = new Object();
+
+ /** number of batches run; used to generated batch IDs */
+ private static long batchCount = 0;
+
+ /** maps batch ID to batch data */
+ private static final Map<Long,Batch> batchTable =
+ Collections.synchronizedMap(new HashMap<Long,Batch>());
+
+ /** stream to output error messages and other diagnostics to */
+ private final PrintStream out;
+
+ /** name of this program, to use in error messages */
+ private final String program;
+
+ /**
+ * Command line entry point.
+ **/
+ public static void main(String[] args) {
+ Main rmic = new Main(System.err, "rmic");
+ System.exit(rmic.compile(args) ? 0 : 1);
+ }
+
+ /**
+ * Creates a Main instance that writes output to the specified
+ * stream. The specified program name is used in error messages.
+ **/
+ public Main(OutputStream out, String program) {
+ this.out = out instanceof PrintStream ?
+ (PrintStream) out : new PrintStream(out);
+ this.program = program;
+ }
+
+ /**
+ * Compiles a batch of input classes, as given by the specified
+ * command line arguments. Protocol-specific generators are
+ * determined by the choice options on the command line. Returns
+ * true if successful, or false if an error occurred.
+ *
+ * NOTE: This method is retained for transitional consistency with
+ * previous implementations.
+ **/
+ public boolean compile(String[] args) {
+ long startTime = System.currentTimeMillis();
+
+ long batchID;
+ synchronized (batchCountLock) {
+ batchID = batchCount++; // assign batch ID
+ }
+
+ // process command line
+ Batch batch = parseArgs(args);
+ if (batch == null) {
+ return false; // terminate if error occurred
+ }
+
+ /*
+ * With the batch data retrievable in the global table, run
+ * javadoc to continue the rest of the batch's compliation as
+ * a doclet.
+ */
+ boolean status;
+ try {
+ batchTable.put(batchID, batch);
+ status = invokeJavadoc(batch, batchID);
+ } finally {
+ batchTable.remove(batchID);
+ }
+
+ if (batch.verbose) {
+ long deltaTime = System.currentTimeMillis() - startTime;
+ output(Resources.getText("rmic.done_in",
+ Long.toString(deltaTime)));
+ }
+
+ return status;
+ }
+
+ /**
+ * Prints the specified string to the output stream of this Main
+ * instance.
+ **/
+ public void output(String msg) {
+ out.println(msg);
+ }
+
+ /**
+ * Prints an error message to the output stream of this Main
+ * instance. The first argument is used as a key in rmic's
+ * resource bundle, and the rest of the arguments are used as
+ * arguments in the formatting of the resource string.
+ **/
+ public void error(String msg, String... args) {
+ output(Resources.getText(msg, args));
+ }
+
+ /**
+ * Prints rmic's usage message to the output stream of this Main
+ * instance.
+ *
+ * This method is public so that it can be used by the "parseArgs"
+ * methods of Generator implementations.
+ **/
+ public void usage() {
+ error("rmic.usage", program);
+ }
+
+ /**
+ * Processes rmic command line arguments. Returns a Batch object
+ * representing the command line arguments if successful, or null
+ * if an error occurred. Processed elements of the args array are
+ * set to null.
+ **/
+ private Batch parseArgs(String[] args) {
+ Batch batch = new Batch();
+
+ /*
+ * Pre-process command line for @file arguments.
+ */
+ try {
+ args = CommandLine.parse(args);
+ } catch (FileNotFoundException e) {
+ error("rmic.cant.read", e.getMessage());
+ return null;
+ } catch (IOException e) {
+ e.printStackTrace(out);
+ return null;
+ }
+
+ for (int i = 0; i < args.length; i++) {
+
+ if (args[i] == null) {
+ // already processed by a generator
+ continue;
+
+ } else if (args[i].equals("-Xnew")) {
+ // we're already using the "new" implementation
+ args[i] = null;
+
+ } else if (args[i].equals("-show")) {
+ // obselete: fail
+ error("rmic.option.unsupported", args[i]);
+ usage();
+ return null;
+
+ } else if (args[i].equals("-O")) {
+ // obselete: warn but tolerate
+ error("rmic.option.unsupported", args[i]);
+ args[i] = null;
+
+ } else if (args[i].equals("-debug")) {
+ // obselete: warn but tolerate
+ error("rmic.option.unsupported", args[i]);
+ args[i] = null;
+
+ } else if (args[i].equals("-depend")) {
+ // obselete: warn but tolerate
+ // REMIND: should this fail instead?
+ error("rmic.option.unsupported", args[i]);
+ args[i] = null;
+
+ } else if (args[i].equals("-keep") ||
+ args[i].equals("-keepgenerated"))
+ {
+ batch.keepGenerated = true;
+ args[i] = null;
+
+ } else if (args[i].equals("-g")) {
+ batch.debug = true;
+ args[i] = null;
+
+ } else if (args[i].equals("-nowarn")) {
+ batch.noWarn = true;
+ args[i] = null;
+
+ } else if (args[i].equals("-nowrite")) {
+ batch.noWrite = true;
+ args[i] = null;
+
+ } else if (args[i].equals("-verbose")) {
+ batch.verbose = true;
+ args[i] = null;
+
+ } else if (args[i].equals("-Xnocompile")) {
+ batch.noCompile = true;
+ batch.keepGenerated = true;
+ args[i] = null;
+
+ } else if (args[i].equals("-bootclasspath")) {
+ if ((i + 1) >= args.length) {
+ error("rmic.option.requires.argument", args[i]);
+ usage();
+ return null;
+ }
+ if (batch.bootClassPath != null) {
+ error("rmic.option.already.seen", args[i]);
+ usage();
+ return null;
+ }
+ args[i] = null;
+ batch.bootClassPath = args[++i];
+ assert batch.bootClassPath != null;
+ args[i] = null;
+
+ } else if (args[i].equals("-extdirs")) {
+ if ((i + 1) >= args.length) {
+ error("rmic.option.requires.argument", args[i]);
+ usage();
+ return null;
+ }
+ if (batch.extDirs != null) {
+ error("rmic.option.already.seen", args[i]);
+ usage();
+ return null;
+ }
+ args[i] = null;
+ batch.extDirs = args[++i];
+ assert batch.extDirs != null;
+ args[i] = null;
+
+ } else if (args[i].equals("-classpath")) {
+ if ((i + 1) >= args.length) {
+ error("rmic.option.requires.argument", args[i]);
+ usage();
+ return null;
+ }
+ if (batch.classPath != null) {
+ error("rmic.option.already.seen", args[i]);
+ usage();
+ return null;
+ }
+ args[i] = null;
+ batch.classPath = args[++i];
+ assert batch.classPath != null;
+ args[i] = null;
+
+ } else if (args[i].equals("-d")) {
+ if ((i + 1) >= args.length) {
+ error("rmic.option.requires.argument", args[i]);
+ usage();
+ return null;
+ }
+ if (batch.destDir != null) {
+ error("rmic.option.already.seen", args[i]);
+ usage();
+ return null;
+ }
+ args[i] = null;
+ batch.destDir = new File(args[++i]);
+ assert batch.destDir != null;
+ args[i] = null;
+ if (!batch.destDir.exists()) {
+ error("rmic.no.such.directory", batch.destDir.getPath());
+ usage();
+ return null;
+ }
+
+ } else if (args[i].equals("-v1.1") ||
+ args[i].equals("-vcompat") ||
+ args[i].equals("-v1.2"))
+ {
+ Generator gen = new JrmpGenerator();
+ batch.generators.add(gen);
+ // JrmpGenerator only requires base BatchEnvironment class
+ if (!gen.parseArgs(args, this)) {
+ return null;
+ }
+
+ } else if (args[i].equalsIgnoreCase("-iiop")) {
+ error("rmic.option.unimplemented", args[i]);
+ return null;
+
+ // Generator gen = new IiopGenerator();
+ // batch.generators.add(gen);
+ // if (!batch.envClass.isAssignableFrom(gen.envClass())) {
+ // error("rmic.cannot.use.both",
+ // batch.envClass.getName(), gen.envClass().getName());
+ // return null;
+ // }
+ // batch.envClass = gen.envClass();
+ // if (!gen.parseArgs(args, this)) {
+ // return null;
+ // }
+
+ } else if (args[i].equalsIgnoreCase("-idl")) {
+ error("rmic.option.unimplemented", args[i]);
+ return null;
+
+ // see implementation sketch above
+
+ } else if (args[i].equalsIgnoreCase("-xprint")) {
+ error("rmic.option.unimplemented", args[i]);
+ return null;
+
+ // see implementation sketch above
+ }
+ }
+
+ /*
+ * At this point, all that remains non-null in the args
+ * array are input class names or illegal options.
+ */
+ for (int i = 0; i < args.length; i++) {
+ if (args[i] != null) {
+ if (args[i].startsWith("-")) {
+ error("rmic.no.such.option", args[i]);
+ usage();
+ return null;
+ } else {
+ batch.classes.add(args[i]);
+ }
+ }
+ }
+ if (batch.classes.isEmpty()) {
+ usage();
+ return null;
+ }
+
+ /*
+ * If options did not specify at least one protocol-specific
+ * generator, then JRMP is the default.
+ */
+ if (batch.generators.isEmpty()) {
+ batch.generators.add(new JrmpGenerator());
+ }
+ return batch;
+ }
+
+ /**
+ * Doclet class entry point.
+ **/
+ public static boolean start(RootDoc rootDoc) {
+
+ /*
+ * Find batch ID among javadoc options, and retrieve
+ * corresponding batch data from global table.
+ */
+ long batchID = -1;
+ for (String[] option : rootDoc.options()) {
+ if (option[0].equals("-batchID")) {
+ try {
+ batchID = Long.parseLong(option[1]);
+ } catch (NumberFormatException e) {
+ throw new AssertionError(e);
+ }
+ }
+ }
+ Batch batch = batchTable.get(batchID);
+ assert batch != null;
+
+ /*
+ * Construct batch environment using class agreed upon by
+ * generator implementations.
+ */
+ BatchEnvironment env;
+ try {
+ Constructor<? extends BatchEnvironment> cons =
+ batch.envClass.getConstructor(new Class[] { RootDoc.class });
+ env = cons.newInstance(rootDoc);
+ } catch (NoSuchMethodException e) {
+ throw new AssertionError(e);
+ } catch (IllegalAccessException e) {
+ throw new AssertionError(e);
+ } catch (InstantiationException e) {
+ throw new AssertionError(e);
+ } catch (InvocationTargetException e) {
+ throw new AssertionError(e);
+ }
+
+ env.setVerbose(batch.verbose);
+
+ /*
+ * Determine the destination directory (the top of the package
+ * hierarchy) for the output of this batch; if no destination
+ * directory was specified on the command line, then the
+ * default is the current working directory.
+ */
+ File destDir = batch.destDir;
+ if (destDir == null) {
+ destDir = new File(System.getProperty("user.dir"));
+ }
+
+ /*
+ * Run each input class through each generator.
+ */
+ for (String inputClassName : batch.classes) {
+ ClassDoc inputClass = rootDoc.classNamed(inputClassName);
+ try {
+ for (Generator gen : batch.generators) {
+ gen.generate(env, inputClass, destDir);
+ }
+ } catch (NullPointerException e) {
+ /*
+ * We assume that this means that some class that was
+ * needed (perhaps even a bootstrap class) was not
+ * found, and that javadoc has already reported this
+ * as an error. There is nothing for us to do here
+ * but try to continue with the next input class.
+ *
+ * REMIND: More explicit error checking throughout
+ * would be preferable, however.
+ */
+ }
+ }
+
+ /*
+ * Compile any generated source files, if configured to do so.
+ */
+ boolean status = true;
+ List<File> generatedFiles = env.generatedFiles();
+ if (!batch.noCompile && !batch.noWrite && !generatedFiles.isEmpty()) {
+ status = batch.enclosingMain().invokeJavac(batch, generatedFiles);
+ }
+
+ /*
+ * Delete any generated source files, if configured to do so.
+ */
+ if (!batch.keepGenerated) {
+ for (File file : generatedFiles) {
+ file.delete();
+ }
+ }
+
+ return status;
+ }
+
+ /**
+ * Doclet class method that indicates that this doclet class
+ * recognizes (only) the "-batchID" option on the javadoc command
+ * line, and that the "-batchID" option comprises two arguments on
+ * the javadoc command line.
+ **/
+ public static int optionLength(String option) {
+ if (option.equals("-batchID")) {
+ return 2;
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Runs the javadoc tool to invoke this class as a doclet, passing
+ * command line options derived from the specified batch data and
+ * indicating the specified batch ID.
+ *
+ * NOTE: This method currently uses a J2SE-internal API to run
+ * javadoc. If and when there is a J2SE API for invoking SDK
+ * tools, this method should be updated to use that API instead.
+ **/
+ private boolean invokeJavadoc(Batch batch, long batchID) {
+ List<String> javadocArgs = new ArrayList<String>();
+
+ // include all types, regardless of language-level access
+ javadocArgs.add("-private");
+
+ // inputs are class names, not source files
+ javadocArgs.add("-Xclasses");
+
+ // reproduce relevant options from rmic invocation
+ if (batch.verbose) {
+ javadocArgs.add("-verbose");
+ }
+ if (batch.bootClassPath != null) {
+ javadocArgs.add("-bootclasspath");
+ javadocArgs.add(batch.bootClassPath);
+ }
+ if (batch.extDirs != null) {
+ javadocArgs.add("-extdirs");
+ javadocArgs.add(batch.extDirs);
+ }
+ if (batch.classPath != null) {
+ javadocArgs.add("-classpath");
+ javadocArgs.add(batch.classPath);
+ }
+
+ // specify batch ID
+ javadocArgs.add("-batchID");
+ javadocArgs.add(Long.toString(batchID));
+
+ /*
+ * Run javadoc on union of rmic input classes and all
+ * generators' bootstrap classes, so that they will all be
+ * available to the doclet code.
+ */
+ Set<String> classNames = new HashSet<String>();
+ for (Generator gen : batch.generators) {
+ classNames.addAll(gen.bootstrapClassNames());
+ }
+ classNames.addAll(batch.classes);
+ for (String s : classNames) {
+ javadocArgs.add(s);
+ }
+
+ // run javadoc with our program name and output stream
+ int status = com.sun.tools.javadoc.Main.execute(
+ program,
+ new PrintWriter(out, true),
+ new PrintWriter(out, true),
+ new PrintWriter(out, true),
+ this.getClass().getName(), // doclet class is this class
+ javadocArgs.toArray(new String[javadocArgs.size()]));
+ return status == 0;
+ }
+
+ /**
+ * Runs the javac tool to compile the specified source files,
+ * passing command line options derived from the specified batch
+ * data.
+ *
+ * NOTE: This method currently uses a J2SE-internal API to run
+ * javac. If and when there is a J2SE API for invoking SDK tools,
+ * this method should be updated to use that API instead.
+ **/
+ private boolean invokeJavac(Batch batch, List<File> files) {
+ List<String> javacArgs = new ArrayList<String>();
+
+ // rmic never wants to display javac warnings
+ javacArgs.add("-nowarn");
+
+ // reproduce relevant options from rmic invocation
+ if (batch.debug) {
+ javacArgs.add("-g");
+ }
+ if (batch.verbose) {
+ javacArgs.add("-verbose");
+ }
+ if (batch.bootClassPath != null) {
+ javacArgs.add("-bootclasspath");
+ javacArgs.add(batch.bootClassPath);
+ }
+ if (batch.extDirs != null) {
+ javacArgs.add("-extdirs");
+ javacArgs.add(batch.extDirs);
+ }
+ if (batch.classPath != null) {
+ javacArgs.add("-classpath");
+ javacArgs.add(batch.classPath);
+ }
+
+ /*
+ * For now, rmic still always produces class files that have a
+ * class file format version compatible with JDK 1.1.
+ */
+ javacArgs.add("-source");
+ javacArgs.add("1.3");
+ javacArgs.add("-target");
+ javacArgs.add("1.1");
+
+ // add source files to compile
+ for (File file : files) {
+ javacArgs.add(file.getPath());
+ }
+
+ // run javac with our output stream
+ int status = com.sun.tools.javac.Main.compile(
+ javacArgs.toArray(new String[javacArgs.size()]),
+ new PrintWriter(out, true));
+ return status == 0;
+ }
+
+ /**
+ * The data for an rmic compliation batch: the processed command
+ * line arguments.
+ **/
+ private class Batch {
+ boolean keepGenerated = false; // -keep or -keepgenerated
+ boolean debug = false; // -g
+ boolean noWarn = false; // -nowarn
+ boolean noWrite = false; // -nowrite
+ boolean verbose = false; // -verbose
+ boolean noCompile = false; // -Xnocompile
+ String bootClassPath = null; // -bootclasspath
+ String extDirs = null; // -extdirs
+ String classPath = null; // -classpath
+ File destDir = null; // -d
+ List<Generator> generators = new ArrayList<Generator>();
+ Class<? extends BatchEnvironment> envClass = BatchEnvironment.class;
+ List<String> classes = new ArrayList<String>();
+
+ Batch() { }
+
+ /**
+ * Returns the Main instance for this batch.
+ **/
+ Main enclosingMain() {
+ return Main.this;
+ }
+ }
+}