Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/rmi/rmic/newrmic/Main.java
38923 views
/*1* Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.rmi.rmic.newrmic;2627import com.sun.javadoc.ClassDoc;28import com.sun.javadoc.RootDoc;29import java.io.File;30import java.io.FileNotFoundException;31import java.io.IOException;32import java.io.OutputStream;33import java.io.PrintStream;34import java.io.PrintWriter;35import java.lang.reflect.Constructor;36import java.lang.reflect.InvocationTargetException;37import java.util.ArrayList;38import java.util.Collections;39import java.util.HashMap;40import java.util.HashSet;41import java.util.List;42import java.util.Map;43import java.util.Set;44import sun.rmi.rmic.newrmic.jrmp.JrmpGenerator;45import sun.tools.util.CommandLine;4647/**48* The rmic front end. This class contains the "main" method for rmic49* command line invocation.50*51* A Main instance contains the stream to output error messages and52* other diagnostics to.53*54* An rmic compilation batch (for example, one rmic command line55* invocation) is executed by invoking the "compile" method of a Main56* instance.57*58* WARNING: The contents of this source file are not part of any59* supported API. Code that depends on them does so at its own risk:60* they are subject to change or removal without notice.61*62* NOTE: If and when there is a J2SE API for invoking SDK tools, this63* class should be updated to support that API.64*65* NOTE: This class is the front end for a "new" rmic implementation,66* which uses javadoc and the doclet API for reading class files and67* javac for compiling generated source files. This implementation is68* incomplete: it lacks any CORBA-based back end implementations, and69* thus the command line options "-idl", "-iiop", and their related70* options are not yet supported. The front end for the "old",71* oldjavac-based rmic implementation is sun.rmi.rmic.Main.72*73* @author Peter Jones74**/75public class Main {7677/*78* Implementation note:79*80* In order to use the doclet API to read class files, much of81* this implementation of rmic executes as a doclet within an82* invocation of javadoc. This class is used as the doclet class83* for such javadoc invocations, via its static "start" and84* "optionLength" methods. There is one javadoc invocation per85* rmic compilation batch.86*87* The only guaranteed way to pass data to a doclet through a88* javadoc invocation is through doclet-specific options on the89* javadoc "command line". Rather than passing numerous pieces of90* individual data in string form as javadoc options, we use a91* single doclet-specific option ("-batchID") to pass a numeric92* identifier that uniquely identifies the rmic compilation batch93* that the javadoc invocation is for, and that identifier can94* then be used as a key in a global table to retrieve an object95* containing all of batch-specific data (rmic command line96* arguments, etc.).97*/9899/** guards "batchCount" */100private static final Object batchCountLock = new Object();101102/** number of batches run; used to generated batch IDs */103private static long batchCount = 0;104105/** maps batch ID to batch data */106private static final Map<Long,Batch> batchTable =107Collections.synchronizedMap(new HashMap<Long,Batch>());108109/** stream to output error messages and other diagnostics to */110private final PrintStream out;111112/** name of this program, to use in error messages */113private final String program;114115/**116* Command line entry point.117**/118public static void main(String[] args) {119Main rmic = new Main(System.err, "rmic");120System.exit(rmic.compile(args) ? 0 : 1);121}122123/**124* Creates a Main instance that writes output to the specified125* stream. The specified program name is used in error messages.126**/127public Main(OutputStream out, String program) {128this.out = out instanceof PrintStream ?129(PrintStream) out : new PrintStream(out);130this.program = program;131}132133/**134* Compiles a batch of input classes, as given by the specified135* command line arguments. Protocol-specific generators are136* determined by the choice options on the command line. Returns137* true if successful, or false if an error occurred.138*139* NOTE: This method is retained for transitional consistency with140* previous implementations.141**/142public boolean compile(String[] args) {143long startTime = System.currentTimeMillis();144145long batchID;146synchronized (batchCountLock) {147batchID = batchCount++; // assign batch ID148}149150// process command line151Batch batch = parseArgs(args);152if (batch == null) {153return false; // terminate if error occurred154}155156/*157* With the batch data retrievable in the global table, run158* javadoc to continue the rest of the batch's compliation as159* a doclet.160*/161boolean status;162try {163batchTable.put(batchID, batch);164status = invokeJavadoc(batch, batchID);165} finally {166batchTable.remove(batchID);167}168169if (batch.verbose) {170long deltaTime = System.currentTimeMillis() - startTime;171output(Resources.getText("rmic.done_in",172Long.toString(deltaTime)));173}174175return status;176}177178/**179* Prints the specified string to the output stream of this Main180* instance.181**/182public void output(String msg) {183out.println(msg);184}185186/**187* Prints an error message to the output stream of this Main188* instance. The first argument is used as a key in rmic's189* resource bundle, and the rest of the arguments are used as190* arguments in the formatting of the resource string.191**/192public void error(String msg, String... args) {193output(Resources.getText(msg, args));194}195196/**197* Prints rmic's usage message to the output stream of this Main198* instance.199*200* This method is public so that it can be used by the "parseArgs"201* methods of Generator implementations.202**/203public void usage() {204error("rmic.usage", program);205}206207/**208* Processes rmic command line arguments. Returns a Batch object209* representing the command line arguments if successful, or null210* if an error occurred. Processed elements of the args array are211* set to null.212**/213private Batch parseArgs(String[] args) {214Batch batch = new Batch();215216/*217* Pre-process command line for @file arguments.218*/219try {220args = CommandLine.parse(args);221} catch (FileNotFoundException e) {222error("rmic.cant.read", e.getMessage());223return null;224} catch (IOException e) {225e.printStackTrace(out);226return null;227}228229for (int i = 0; i < args.length; i++) {230231if (args[i] == null) {232// already processed by a generator233continue;234235} else if (args[i].equals("-Xnew")) {236// we're already using the "new" implementation237args[i] = null;238239} else if (args[i].equals("-show")) {240// obselete: fail241error("rmic.option.unsupported", args[i]);242usage();243return null;244245} else if (args[i].equals("-O")) {246// obselete: warn but tolerate247error("rmic.option.unsupported", args[i]);248args[i] = null;249250} else if (args[i].equals("-debug")) {251// obselete: warn but tolerate252error("rmic.option.unsupported", args[i]);253args[i] = null;254255} else if (args[i].equals("-depend")) {256// obselete: warn but tolerate257// REMIND: should this fail instead?258error("rmic.option.unsupported", args[i]);259args[i] = null;260261} else if (args[i].equals("-keep") ||262args[i].equals("-keepgenerated"))263{264batch.keepGenerated = true;265args[i] = null;266267} else if (args[i].equals("-g")) {268batch.debug = true;269args[i] = null;270271} else if (args[i].equals("-nowarn")) {272batch.noWarn = true;273args[i] = null;274275} else if (args[i].equals("-nowrite")) {276batch.noWrite = true;277args[i] = null;278279} else if (args[i].equals("-verbose")) {280batch.verbose = true;281args[i] = null;282283} else if (args[i].equals("-Xnocompile")) {284batch.noCompile = true;285batch.keepGenerated = true;286args[i] = null;287288} else if (args[i].equals("-bootclasspath")) {289if ((i + 1) >= args.length) {290error("rmic.option.requires.argument", args[i]);291usage();292return null;293}294if (batch.bootClassPath != null) {295error("rmic.option.already.seen", args[i]);296usage();297return null;298}299args[i] = null;300batch.bootClassPath = args[++i];301assert batch.bootClassPath != null;302args[i] = null;303304} else if (args[i].equals("-extdirs")) {305if ((i + 1) >= args.length) {306error("rmic.option.requires.argument", args[i]);307usage();308return null;309}310if (batch.extDirs != null) {311error("rmic.option.already.seen", args[i]);312usage();313return null;314}315args[i] = null;316batch.extDirs = args[++i];317assert batch.extDirs != null;318args[i] = null;319320} else if (args[i].equals("-classpath")) {321if ((i + 1) >= args.length) {322error("rmic.option.requires.argument", args[i]);323usage();324return null;325}326if (batch.classPath != null) {327error("rmic.option.already.seen", args[i]);328usage();329return null;330}331args[i] = null;332batch.classPath = args[++i];333assert batch.classPath != null;334args[i] = null;335336} else if (args[i].equals("-d")) {337if ((i + 1) >= args.length) {338error("rmic.option.requires.argument", args[i]);339usage();340return null;341}342if (batch.destDir != null) {343error("rmic.option.already.seen", args[i]);344usage();345return null;346}347args[i] = null;348batch.destDir = new File(args[++i]);349assert batch.destDir != null;350args[i] = null;351if (!batch.destDir.exists()) {352error("rmic.no.such.directory", batch.destDir.getPath());353usage();354return null;355}356357} else if (args[i].equals("-v1.1") ||358args[i].equals("-vcompat") ||359args[i].equals("-v1.2"))360{361Generator gen = new JrmpGenerator();362batch.generators.add(gen);363// JrmpGenerator only requires base BatchEnvironment class364if (!gen.parseArgs(args, this)) {365return null;366}367368} else if (args[i].equalsIgnoreCase("-iiop")) {369error("rmic.option.unimplemented", args[i]);370return null;371372// Generator gen = new IiopGenerator();373// batch.generators.add(gen);374// if (!batch.envClass.isAssignableFrom(gen.envClass())) {375// error("rmic.cannot.use.both",376// batch.envClass.getName(), gen.envClass().getName());377// return null;378// }379// batch.envClass = gen.envClass();380// if (!gen.parseArgs(args, this)) {381// return null;382// }383384} else if (args[i].equalsIgnoreCase("-idl")) {385error("rmic.option.unimplemented", args[i]);386return null;387388// see implementation sketch above389390} else if (args[i].equalsIgnoreCase("-xprint")) {391error("rmic.option.unimplemented", args[i]);392return null;393394// see implementation sketch above395}396}397398/*399* At this point, all that remains non-null in the args400* array are input class names or illegal options.401*/402for (int i = 0; i < args.length; i++) {403if (args[i] != null) {404if (args[i].startsWith("-")) {405error("rmic.no.such.option", args[i]);406usage();407return null;408} else {409batch.classes.add(args[i]);410}411}412}413if (batch.classes.isEmpty()) {414usage();415return null;416}417418/*419* If options did not specify at least one protocol-specific420* generator, then JRMP is the default.421*/422if (batch.generators.isEmpty()) {423batch.generators.add(new JrmpGenerator());424}425return batch;426}427428/**429* Doclet class entry point.430**/431public static boolean start(RootDoc rootDoc) {432433/*434* Find batch ID among javadoc options, and retrieve435* corresponding batch data from global table.436*/437long batchID = -1;438for (String[] option : rootDoc.options()) {439if (option[0].equals("-batchID")) {440try {441batchID = Long.parseLong(option[1]);442} catch (NumberFormatException e) {443throw new AssertionError(e);444}445}446}447Batch batch = batchTable.get(batchID);448assert batch != null;449450/*451* Construct batch environment using class agreed upon by452* generator implementations.453*/454BatchEnvironment env;455try {456Constructor<? extends BatchEnvironment> cons =457batch.envClass.getConstructor(new Class<?>[] { RootDoc.class });458env = cons.newInstance(rootDoc);459} catch (NoSuchMethodException e) {460throw new AssertionError(e);461} catch (IllegalAccessException e) {462throw new AssertionError(e);463} catch (InstantiationException e) {464throw new AssertionError(e);465} catch (InvocationTargetException e) {466throw new AssertionError(e);467}468469env.setVerbose(batch.verbose);470471/*472* Determine the destination directory (the top of the package473* hierarchy) for the output of this batch; if no destination474* directory was specified on the command line, then the475* default is the current working directory.476*/477File destDir = batch.destDir;478if (destDir == null) {479destDir = new File(System.getProperty("user.dir"));480}481482/*483* Run each input class through each generator.484*/485for (String inputClassName : batch.classes) {486ClassDoc inputClass = rootDoc.classNamed(inputClassName);487try {488for (Generator gen : batch.generators) {489gen.generate(env, inputClass, destDir);490}491} catch (NullPointerException e) {492/*493* We assume that this means that some class that was494* needed (perhaps even a bootstrap class) was not495* found, and that javadoc has already reported this496* as an error. There is nothing for us to do here497* but try to continue with the next input class.498*499* REMIND: More explicit error checking throughout500* would be preferable, however.501*/502}503}504505/*506* Compile any generated source files, if configured to do so.507*/508boolean status = true;509List<File> generatedFiles = env.generatedFiles();510if (!batch.noCompile && !batch.noWrite && !generatedFiles.isEmpty()) {511status = batch.enclosingMain().invokeJavac(batch, generatedFiles);512}513514/*515* Delete any generated source files, if configured to do so.516*/517if (!batch.keepGenerated) {518for (File file : generatedFiles) {519file.delete();520}521}522523return status;524}525526/**527* Doclet class method that indicates that this doclet class528* recognizes (only) the "-batchID" option on the javadoc command529* line, and that the "-batchID" option comprises two arguments on530* the javadoc command line.531**/532public static int optionLength(String option) {533if (option.equals("-batchID")) {534return 2;535} else {536return 0;537}538}539540/**541* Runs the javadoc tool to invoke this class as a doclet, passing542* command line options derived from the specified batch data and543* indicating the specified batch ID.544*545* NOTE: This method currently uses a J2SE-internal API to run546* javadoc. If and when there is a J2SE API for invoking SDK547* tools, this method should be updated to use that API instead.548**/549private boolean invokeJavadoc(Batch batch, long batchID) {550List<String> javadocArgs = new ArrayList<String>();551552// include all types, regardless of language-level access553javadocArgs.add("-private");554555// inputs are class names, not source files556javadocArgs.add("-Xclasses");557558// reproduce relevant options from rmic invocation559if (batch.verbose) {560javadocArgs.add("-verbose");561}562if (batch.bootClassPath != null) {563javadocArgs.add("-bootclasspath");564javadocArgs.add(batch.bootClassPath);565}566if (batch.extDirs != null) {567javadocArgs.add("-extdirs");568javadocArgs.add(batch.extDirs);569}570if (batch.classPath != null) {571javadocArgs.add("-classpath");572javadocArgs.add(batch.classPath);573}574575// specify batch ID576javadocArgs.add("-batchID");577javadocArgs.add(Long.toString(batchID));578579/*580* Run javadoc on union of rmic input classes and all581* generators' bootstrap classes, so that they will all be582* available to the doclet code.583*/584Set<String> classNames = new HashSet<String>();585for (Generator gen : batch.generators) {586classNames.addAll(gen.bootstrapClassNames());587}588classNames.addAll(batch.classes);589for (String s : classNames) {590javadocArgs.add(s);591}592593// run javadoc with our program name and output stream594int status = com.sun.tools.javadoc.Main.execute(595program,596new PrintWriter(out, true),597new PrintWriter(out, true),598new PrintWriter(out, true),599this.getClass().getName(), // doclet class is this class600javadocArgs.toArray(new String[javadocArgs.size()]));601return status == 0;602}603604/**605* Runs the javac tool to compile the specified source files,606* passing command line options derived from the specified batch607* data.608*609* NOTE: This method currently uses a J2SE-internal API to run610* javac. If and when there is a J2SE API for invoking SDK tools,611* this method should be updated to use that API instead.612**/613private boolean invokeJavac(Batch batch, List<File> files) {614List<String> javacArgs = new ArrayList<String>();615616// rmic never wants to display javac warnings617javacArgs.add("-nowarn");618619// reproduce relevant options from rmic invocation620if (batch.debug) {621javacArgs.add("-g");622}623if (batch.verbose) {624javacArgs.add("-verbose");625}626if (batch.bootClassPath != null) {627javacArgs.add("-bootclasspath");628javacArgs.add(batch.bootClassPath);629}630if (batch.extDirs != null) {631javacArgs.add("-extdirs");632javacArgs.add(batch.extDirs);633}634if (batch.classPath != null) {635javacArgs.add("-classpath");636javacArgs.add(batch.classPath);637}638639/*640* For now, rmic still always produces class files that have a641* class file format version compatible with JDK 1.1.642*/643javacArgs.add("-source");644javacArgs.add("1.3");645javacArgs.add("-target");646javacArgs.add("1.1");647648// add source files to compile649for (File file : files) {650javacArgs.add(file.getPath());651}652653// run javac with our output stream654int status = com.sun.tools.javac.Main.compile(655javacArgs.toArray(new String[javacArgs.size()]),656new PrintWriter(out, true));657return status == 0;658}659660/**661* The data for an rmic compliation batch: the processed command662* line arguments.663**/664private class Batch {665boolean keepGenerated = false; // -keep or -keepgenerated666boolean debug = false; // -g667boolean noWarn = false; // -nowarn668boolean noWrite = false; // -nowrite669boolean verbose = false; // -verbose670boolean noCompile = false; // -Xnocompile671String bootClassPath = null; // -bootclasspath672String extDirs = null; // -extdirs673String classPath = null; // -classpath674File destDir = null; // -d675List<Generator> generators = new ArrayList<Generator>();676Class<? extends BatchEnvironment> envClass = BatchEnvironment.class;677List<String> classes = new ArrayList<String>();678679Batch() { }680681/**682* Returns the Main instance for this batch.683**/684Main enclosingMain() {685return Main.this;686}687}688}689690691