diff options
Diffstat (limited to 'src/share/classes/sun/rmi/rmic/Main.java')
-rw-r--r-- | src/share/classes/sun/rmi/rmic/Main.java | 884 |
1 files changed, 884 insertions, 0 deletions
diff --git a/src/share/classes/sun/rmi/rmic/Main.java b/src/share/classes/sun/rmi/rmic/Main.java new file mode 100644 index 000000000..bb9719aaf --- /dev/null +++ b/src/share/classes/sun/rmi/rmic/Main.java @@ -0,0 +1,884 @@ +/* + * Copyright 1996-2007 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. + */ + +/* + * Licensed Materials - Property of IBM + * RMI-IIOP v1.0 + * Copyright IBM Corp. 1998 1999 All Rights Reserved + * + */ + +package sun.rmi.rmic; + +import java.util.Vector; +import java.util.Enumeration; +import java.util.ResourceBundle; +import java.util.StringTokenizer; +import java.util.MissingResourceException; + +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.IOException; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.ByteArrayOutputStream; + +import sun.tools.java.ClassFile; +import sun.tools.java.ClassDefinition; +import sun.tools.java.ClassDeclaration; +import sun.tools.java.ClassNotFound; +import sun.tools.java.Identifier; +import sun.tools.java.ClassPath; + +import sun.tools.javac.SourceClass; +import sun.tools.util.CommandLine; +import java.lang.reflect.Constructor; +import java.util.Properties; + +/** + * Main "rmic" program. + * + * 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. + */ +public class Main implements sun.rmi.rmic.Constants { + String sourcePathArg; + String sysClassPathArg; + String extDirsArg; + String classPathString; + File destDir; + int flags; + long tm; + Vector classes; + boolean nowrite; + boolean nocompile; + boolean keepGenerated; + boolean status; + String[] generatorArgs; + Vector generators; + Class environmentClass = BatchEnvironment.class; + boolean iiopGeneration = false; + + /** + * Name of the program. + */ + String program; + + /** + * The stream where error message are printed. + */ + OutputStream out; + + /** + * Constructor. + */ + public Main(OutputStream out, String program) { + this.out = out; + this.program = program; + } + + /** + * Output a message. + */ + public void output(String msg) { + PrintStream out = + this.out instanceof PrintStream ? (PrintStream)this.out + : new PrintStream(this.out, true); + out.println(msg); + } + + /** + * Top level error message. This method is called when the + * environment could not be set up yet. + */ + public void error(String msg) { + output(getText(msg)); + } + + public void error(String msg, String arg1) { + output(getText(msg, arg1)); + } + + public void error(String msg, String arg1, String arg2) { + output(getText(msg, arg1, arg2)); + } + + /** + * Usage + */ + public void usage() { + error("rmic.usage", program); + } + + /** + * Run the compiler + */ + public synchronized boolean compile(String argv[]) { + + /* + * Handle internal option to use the new (and incomplete) rmic + * implementation. This option is handled here, rather than + * in parseArgs, so that none of the arguments will be nulled + * before delegating to the new implementation. + */ + for (int i = 0; i < argv.length; i++) { + if (argv[i].equals("-Xnew")) { + return (new sun.rmi.rmic.newrmic.Main(out, + program)).compile(argv); + } + } + + if (!parseArgs(argv)) { + return false; + } + + if (classes.size() == 0) { + usage(); + return false; + } + + return doCompile(); + } + + /** + * Get the destination directory. + */ + public File getDestinationDir() { + return destDir; + } + + /** + * Parse the arguments for compile. + */ + public boolean parseArgs(String argv[]) { + sourcePathArg = null; + sysClassPathArg = null; + extDirsArg = null; + + classPathString = null; + destDir = null; + flags = F_WARNINGS; + tm = System.currentTimeMillis(); + classes = new Vector(); + nowrite = false; + nocompile = false; + keepGenerated = false; + generatorArgs = getArray("generator.args",true); + if (generatorArgs == null) { + return false; + } + generators = new Vector(); + + // Pre-process command line for @file arguments + try { + argv = CommandLine.parse(argv); + } catch (FileNotFoundException e) { + error("rmic.cant.read", e.getMessage()); + return false; + } catch (IOException e) { + e.printStackTrace(out instanceof PrintStream ? + (PrintStream) out : + new PrintStream(out, true)); + return false; + } + + // Parse arguments + for (int i = 0 ; i < argv.length ; i++) { + if (argv[i] != null) { + if (argv[i].equals("-g")) { + flags &= ~F_OPT; + flags |= F_DEBUG_LINES | F_DEBUG_VARS; + argv[i] = null; + } else if (argv[i].equals("-O")) { + flags &= ~F_DEBUG_LINES; + flags &= ~F_DEBUG_VARS; + flags |= F_OPT | F_DEPENDENCIES; + argv[i] = null; + } else if (argv[i].equals("-nowarn")) { + flags &= ~F_WARNINGS; + argv[i] = null; + } else if (argv[i].equals("-debug")) { + flags |= F_DUMP; + argv[i] = null; + } else if (argv[i].equals("-depend")) { + flags |= F_DEPENDENCIES; + argv[i] = null; + } else if (argv[i].equals("-verbose")) { + flags |= F_VERBOSE; + argv[i] = null; + } else if (argv[i].equals("-nowrite")) { + nowrite = true; + argv[i] = null; + } else if (argv[i].equals("-Xnocompile")) { + nocompile = true; + keepGenerated = true; + argv[i] = null; + } else if (argv[i].equals("-keep") || + argv[i].equals("-keepgenerated")) { + keepGenerated = true; + argv[i] = null; + } else if (argv[i].equals("-show")) { + error("rmic.option.unsupported", "-show"); + usage(); + return false; + } else if (argv[i].equals("-classpath")) { + if ((i + 1) < argv.length) { + if (classPathString != null) { + error("rmic.option.already.seen", "-classpath"); + usage(); + return false; + } + argv[i] = null; + classPathString = argv[++i]; + argv[i] = null; + } else { + error("rmic.option.requires.argument", "-classpath"); + usage(); + return false; + } + } else if (argv[i].equals("-sourcepath")) { + if ((i + 1) < argv.length) { + if (sourcePathArg != null) { + error("rmic.option.already.seen", "-sourcepath"); + usage(); + return false; + } + argv[i] = null; + sourcePathArg = argv[++i]; + argv[i] = null; + } else { + error("rmic.option.requires.argument", "-sourcepath"); + usage(); + return false; + } + } else if (argv[i].equals("-bootclasspath")) { + if ((i + 1) < argv.length) { + if (sysClassPathArg != null) { + error("rmic.option.already.seen", "-bootclasspath"); + usage(); + return false; + } + argv[i] = null; + sysClassPathArg = argv[++i]; + argv[i] = null; + } else { + error("rmic.option.requires.argument", "-bootclasspath"); + usage(); + return false; + } + } else if (argv[i].equals("-extdirs")) { + if ((i + 1) < argv.length) { + if (extDirsArg != null) { + error("rmic.option.already.seen", "-extdirs"); + usage(); + return false; + } + argv[i] = null; + extDirsArg = argv[++i]; + argv[i] = null; + } else { + error("rmic.option.requires.argument", "-extdirs"); + usage(); + return false; + } + } else if (argv[i].equals("-d")) { + if ((i + 1) < argv.length) { + if (destDir != null) { + error("rmic.option.already.seen", "-d"); + usage(); + return false; + } + argv[i] = null; + destDir = new File(argv[++i]); + argv[i] = null; + if (!destDir.exists()) { + error("rmic.no.such.directory", destDir.getPath()); + usage(); + return false; + } + } else { + error("rmic.option.requires.argument", "-d"); + usage(); + return false; + } + } else { + if (!checkGeneratorArg(argv,i)) { + usage(); + return false; + } + } + } + } + + + // Now that all generators have had a chance at the args, + // scan what's left for classes and illegal args... + + for (int i = 0; i < argv.length; i++) { + if (argv[i] != null) { + if (argv[i].startsWith("-")) { + error("rmic.no.such.option", argv[i]); + usage(); + return false; + } else { + classes.addElement(argv[i]); + } + } + } + + + // If the generators vector is empty, add the default generator... + + if (generators.size() == 0) { + addGenerator("default"); + } + + return true; + } + + /** + * If this argument is for a generator, instantiate it, call + * parseArgs(...) and add generator to generators vector. + * Returns false on error. + */ + protected boolean checkGeneratorArg(String[] argv, int currentIndex) { + boolean result = true; + if (argv[currentIndex].startsWith("-")) { + String arg = argv[currentIndex].substring(1).toLowerCase(); // Remove '-' + for (int i = 0; i < generatorArgs.length; i++) { + if (arg.equalsIgnoreCase(generatorArgs[i])) { + // Got a match, add Generator and call parseArgs... + Generator gen = addGenerator(arg); + if (gen == null) { + return false; + } + result = gen.parseArgs(argv,this); + break; + } + } + } + return result; + } + + /** + * Instantiate and add a generator to the generators array. + */ + protected Generator addGenerator(String arg) { + + Generator gen; + + // Create an instance of the generator and add it to + // the array... + + String className = getString("generator.class." + arg); + if (className == null) { + error("rmic.missing.property",arg); + return null; + } + + try { + gen = (Generator) Class.forName(className).newInstance(); + } catch (Exception e) { + error("rmic.cannot.instantiate",className); + return null; + } + + generators.addElement(gen); + + // Get the environment required by this generator... + + Class envClass = BatchEnvironment.class; + String env = getString("generator.env." + arg); + if (env != null) { + try { + envClass = Class.forName(env); + + // Is the new class a subclass of the current one? + + if (environmentClass.isAssignableFrom(envClass)) { + + // Yes, so switch to the new one... + + environmentClass = envClass; + + } else { + + // No. Is the current class a subclass of the + // new one? + + if (!envClass.isAssignableFrom(environmentClass)) { + + // No, so it's a conflict... + + error("rmic.cannot.use.both",environmentClass.getName(),envClass.getName()); + return null; + } + } + } catch (ClassNotFoundException e) { + error("rmic.class.not.found",env); + return null; + } + } + + // If this is the iiop stub generator, cache + // that fact for the jrmp generator... + + if (arg.equals("iiop")) { + iiopGeneration = true; + } + return gen; + } + + /** + * Grab a resource string and parse it into an array of strings. Assumes + * comma separated list. + * @param name The resource name. + * @param mustExist If true, throws error if resource does not exist. If + * false and resource does not exist, returns zero element array. + */ + protected String[] getArray(String name, boolean mustExist) { + String[] result = null; + String value = getString(name); + if (value == null) { + if (mustExist) { + error("rmic.resource.not.found",name); + return null; + } else { + return new String[0]; + } + } + + StringTokenizer parser = new StringTokenizer(value,", \t\n\r", false); + int count = parser.countTokens(); + result = new String[count]; + for (int i = 0; i < count; i++) { + result[i] = parser.nextToken(); + } + + return result; + } + + /** + * Get the correct type of BatchEnvironment + */ + public BatchEnvironment getEnv() { + + ClassPath classPath = + BatchEnvironment.createClassPath(classPathString, + sysClassPathArg, + extDirsArg); + BatchEnvironment result = null; + try { + Class[] ctorArgTypes = {OutputStream.class,ClassPath.class,Main.class}; + Object[] ctorArgs = {out,classPath,this}; + Constructor constructor = environmentClass.getConstructor(ctorArgTypes); + result = (BatchEnvironment) constructor.newInstance(ctorArgs); + result.reset(); + } + catch (Exception e) { + error("rmic.cannot.instantiate",environmentClass.getName()); + } + return result; + } + + + /** + * Do the compile with the switches and files already supplied + */ + public boolean doCompile() { + // Create batch environment + BatchEnvironment env = getEnv(); + env.flags |= flags; + + // Set the classfile version numbers + // Compat and 1.1 stubs must retain the old version number. + env.majorVersion = 45; + env.minorVersion = 3; + + // Preload the "out of memory" error string just in case we run + // out of memory during the compile. + String noMemoryErrorString = getText("rmic.no.memory"); + String stackOverflowErrorString = getText("rmic.stack.overflow"); + + try { + /** Load the classes on the command line + * Replace the entries in classes with the ClassDefinition for the class + */ + for (int i = classes.size()-1; i >= 0; i-- ) { + Identifier implClassName = + Identifier.lookup((String)classes.elementAt(i)); + + /* + * Fix bugid 4049354: support using '.' as an inner class + * qualifier on the command line (previously, only mangled + * inner class names were understood, like "pkg.Outer$Inner"). + * + * The following method, also used by "javap", resolves the + * given unmangled inner class name to the appropriate + * internal identifier. For example, it translates + * "pkg.Outer.Inner" to "pkg.Outer. Inner". + */ + implClassName = env.resolvePackageQualifiedName(implClassName); + /* + * But if we use such an internal inner class name identifier + * to load the class definition, the Java compiler will notice + * if the impl class is a "private" inner class and then deny + * skeletons (needed unless "-v1.2" is used) the ability to + * cast to it. To work around this problem, we mangle inner + * class name identifiers to their binary "outer" class name: + * "pkg.Outer. Inner" becomes "pkg.Outer$Inner". + */ + implClassName = Names.mangleClass(implClassName); + + ClassDeclaration decl = env.getClassDeclaration(implClassName); + try { + ClassDefinition def = decl.getClassDefinition(env); + for (int j = 0; j < generators.size(); j++) { + Generator gen = (Generator)generators.elementAt(j); + gen.generate(env, def, destDir); + } + } catch (ClassNotFound ex) { + env.error(0, "rmic.class.not.found", implClassName); + } + + } + + // compile all classes that need compilation + if (!nocompile) { + compileAllClasses(env); + } + } catch (OutOfMemoryError ee) { + // The compiler has run out of memory. Use the error string + // which we preloaded. + env.output(noMemoryErrorString); + return false; + } catch (StackOverflowError ee) { + env.output(stackOverflowErrorString); + return false; + } catch (Error ee) { + // We allow the compiler to take an exception silently if a program + // error has previously been detected. Presumably, this makes the + // compiler more robust in the face of bad error recovery. + if (env.nerrors == 0 || env.dump()) { + env.error(0, "fatal.error"); + ee.printStackTrace(out instanceof PrintStream ? + (PrintStream) out : + new PrintStream(out, true)); + } + } catch (Exception ee) { + if (env.nerrors == 0 || env.dump()) { + env.error(0, "fatal.exception"); + ee.printStackTrace(out instanceof PrintStream ? + (PrintStream) out : + new PrintStream(out, true)); + } + } + + env.flushErrors(); + + boolean status = true; + if (env.nerrors > 0) { + String msg = ""; + if (env.nerrors > 1) { + msg = getText("rmic.errors", env.nerrors); + } else { + msg = getText("rmic.1error"); + } + if (env.nwarnings > 0) { + if (env.nwarnings > 1) { + msg += ", " + getText("rmic.warnings", env.nwarnings); + } else { + msg += ", " + getText("rmic.1warning"); + } + } + output(msg); + status = false; + } else { + if (env.nwarnings > 0) { + if (env.nwarnings > 1) { + output(getText("rmic.warnings", env.nwarnings)); + } else { + output(getText("rmic.1warning")); + } + } + } + + // last step is to delete generated source files + if (!keepGenerated) { + env.deleteGeneratedFiles(); + } + + // We're done + if (env.verbose()) { + tm = System.currentTimeMillis() - tm; + output(getText("rmic.done_in", Long.toString(tm))); + } + + // Shutdown the environment object and release our resources. + // Note that while this is unneccessary when rmic is invoked + // the command line, there are environments in which rmic + // from is invoked within a server process, so resource + // reclamation is important... + + env.shutdown(); + + sourcePathArg = null; + sysClassPathArg = null; + extDirsArg = null; + classPathString = null; + destDir = null; + classes = null; + generatorArgs = null; + generators = null; + environmentClass = null; + program = null; + out = null; + + return status; + } + + /* + * Compile all classes that need to be compiled. + */ + public void compileAllClasses (BatchEnvironment env) + throws ClassNotFound, + IOException, + InterruptedException { + ByteArrayOutputStream buf = new ByteArrayOutputStream(4096); + boolean done; + + do { + done = true; + for (Enumeration e = env.getClasses() ; e.hasMoreElements() ; ) { + ClassDeclaration c = (ClassDeclaration)e.nextElement(); + done = compileClass(c,buf,env); + } + } while (!done); + } + + /* + * Compile a single class. + */ + public boolean compileClass (ClassDeclaration c, + ByteArrayOutputStream buf, + BatchEnvironment env) + throws ClassNotFound, + IOException, + InterruptedException { + boolean done = true; + env.flushErrors(); + SourceClass src; + + switch (c.getStatus()) { + case CS_UNDEFINED: + { + if (!env.dependencies()) { + break; + } + // fall through + } + + case CS_SOURCE: + { + done = false; + env.loadDefinition(c); + if (c.getStatus() != CS_PARSED) { + break; + } + // fall through + } + + case CS_PARSED: + { + if (c.getClassDefinition().isInsideLocal()) { + break; + } + // If we get to here, then compilation is going + // to occur. If the -Xnocompile switch is set + // then fail. Note that this check is required + // here because this method is called from + // generators, not just from within this class... + + if (nocompile) { + throw new IOException("Compilation required, but -Xnocompile option in effect"); + } + + done = false; + + src = (SourceClass)c.getClassDefinition(env); + src.check(env); + c.setDefinition(src, CS_CHECKED); + // fall through + } + + case CS_CHECKED: + { + src = (SourceClass)c.getClassDefinition(env); + // bail out if there were any errors + if (src.getError()) { + c.setDefinition(src, CS_COMPILED); + break; + } + done = false; + buf.reset(); + src.compile(buf); + c.setDefinition(src, CS_COMPILED); + src.cleanup(env); + + if (src.getError() || nowrite) { + break; + } + + String pkgName = c.getName().getQualifier().toString().replace('.', File.separatorChar); + String className = c.getName().getFlatName().toString().replace('.', SIGC_INNERCLASS) + ".class"; + + File file; + if (destDir != null) { + if (pkgName.length() > 0) { + file = new File(destDir, pkgName); + if (!file.exists()) { + file.mkdirs(); + } + file = new File(file, className); + } else { + file = new File(destDir, className); + } + } else { + ClassFile classfile = (ClassFile)src.getSource(); + if (classfile.isZipped()) { + env.error(0, "cant.write", classfile.getPath()); + break; + } + file = new File(classfile.getPath()); + file = new File(file.getParent(), className); + } + + // Create the file + try { + FileOutputStream out = new FileOutputStream(file.getPath()); + buf.writeTo(out); + out.close(); + if (env.verbose()) { + output(getText("rmic.wrote", file.getPath())); + } + } catch (IOException ee) { + env.error(0, "cant.write", file.getPath()); + } + } + } + return done; + } + + /** + * Main program + */ + public static void main(String argv[]) { + Main compiler = new Main(System.out, "rmic"); + System.exit(compiler.compile(argv) ? 0 : 1); + } + + /** + * Return the string value of a named resource in the rmic.properties + * resource bundle. If the resource is not found, null is returned. + */ + public static String getString(String key) { + if (!resourcesInitialized) { + initResources(); + } + + // To enable extensions, search the 'resourcesExt' + // bundle first, followed by the 'resources' bundle... + + if (resourcesExt != null) { + try { + return resourcesExt.getString(key); + } catch (MissingResourceException e) {} + } + + try { + return resources.getString(key); + } catch (MissingResourceException ignore) { + } + return null; + } + + private static boolean resourcesInitialized = false; + private static ResourceBundle resources; + private static ResourceBundle resourcesExt = null; + + private static void initResources() { + try { + resources = + ResourceBundle.getBundle("sun.rmi.rmic.resources.rmic"); + resourcesInitialized = true; + try { + resourcesExt = + ResourceBundle.getBundle("sun.rmi.rmic.resources.rmicext"); + } catch (MissingResourceException e) {} + } catch (MissingResourceException e) { + throw new Error("fatal: missing resource bundle: " + + e.getClassName()); + } + } + + public static String getText(String key) { + String message = getString(key); + if (message == null) { + message = "no text found: \"" + key + "\""; + } + return message; + } + + public static String getText(String key, int num) { + return getText(key, Integer.toString(num), null, null); + } + + public static String getText(String key, String arg0) { + return getText(key, arg0, null, null); + } + + public static String getText(String key, String arg0, String arg1) { + return getText(key, arg0, arg1, null); + } + + public static String getText(String key, + String arg0, String arg1, String arg2) + { + String format = getString(key); + if (format == null) { + format = "no text found: key = \"" + key + "\", " + + "arguments = \"{0}\", \"{1}\", \"{2}\""; + } + + String[] args = new String[3]; + args[0] = (arg0 != null ? arg0.toString() : "null"); + args[1] = (arg1 != null ? arg1.toString() : "null"); + args[2] = (arg2 != null ? arg2.toString() : "null"); + + return java.text.MessageFormat.format(format, args); + } +} |