blob: 2c50e3a397211f96468db66a26c5aed45200a2dd [file] [log] [blame]
/*
* Copyright (c) 1994, 2004, 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 sun.tools.javac;
import sun.tools.java.*;
import sun.tools.util.CommandLine;
// JCOV
import sun.tools.asm.Assembler;
// end JCOV
import java.util.*;
import java.io.*;
import java.text.MessageFormat;
/**
* Main program of the Java compiler
*
* 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.
*
* @deprecated As of J2SE 1.3, the preferred way to compile Java
* language sources is by using the new compiler,
* com.sun.tools.javac.Main.
*/
@Deprecated
public
class Main implements Constants {
/**
* 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;
}
/**
* Exit status.
* We introduce a separate integer status variable, and do not alter the
* convention that 'compile' returns a boolean true upon a successful
* compilation with no errors. (JavaTest relies on this.)
*/
public static final int EXIT_OK = 0; // Compilation completed with no errors.
public static final int EXIT_ERROR = 1; // Compilation completed but reported errors.
public static final int EXIT_CMDERR = 2; // Bad command-line arguments and/or switches.
public static final int EXIT_SYSERR = 3; // System error or resource exhaustion.
public static final int EXIT_ABNORMAL = 4; // Compiler terminated abnormally.
private int exitStatus;
public int getExitStatus() {
return exitStatus;
}
public boolean compilationPerformedSuccessfully() {
return exitStatus == EXIT_OK || exitStatus == EXIT_ERROR;
}
public boolean compilationReportedErrors () {
return exitStatus != EXIT_OK;
}
/**
* Output a message.
*/
private 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.
*/
private void error(String msg) {
exitStatus = EXIT_CMDERR;
output(getText(msg));
}
private void error(String msg, String arg1) {
exitStatus = EXIT_CMDERR;
output(getText(msg, arg1));
}
private void error(String msg, String arg1, String arg2) {
exitStatus = EXIT_CMDERR;
output(getText(msg, arg1, arg2));
}
/**
* Print usage message and make exit status an error.
* Note: 'javac' invoked without any arguments is considered
* be an error.
*/
public void usage_error() {
error("main.usage", program);
}
private static ResourceBundle messageRB;
/**
* Initialize ResourceBundle
*/
static void initResource() {
try {
messageRB =
ResourceBundle.getBundle("sun.tools.javac.resources.javac");
} catch (MissingResourceException e) {
throw new Error("Fatal: Resource for javac is missing");
}
}
/**
* get and format message string from resource
*/
public static String getText(String key) {
return getText(key, (String)null);
}
public static String getText(String key, int num) {
return getText(key, Integer.toString(num));
}
public static String getText(String key, String fixed) {
return getText(key, fixed, null);
}
public static String getText(String key, String fixed1, String fixed2) {
return getText(key, fixed1, fixed2, null);
}
public static String getText(String key, String fixed1,
String fixed2, String fixed3) {
if (messageRB == null) {
initResource();
}
try {
String message = messageRB.getString(key);
return MessageFormat.format(message, fixed1, fixed2, fixed3);
} catch (MissingResourceException e) {
if (fixed1 == null) fixed1 = "null";
if (fixed2 == null) fixed2 = "null";
if (fixed3 == null) fixed3 = "null";
String message = "JAVAC MESSAGE FILE IS BROKEN: key={0}, arguments={1}, {2}, {3}";
return MessageFormat.format(message, key, fixed1, fixed2, fixed3);
}
}
// What major and minor version numbers to use for the -target flag.
// This should grow every time the minor version number accepted by
// the VM is incremented.
private static final String[] releases = { "1.1", "1.2", "1.3", "1.4" };
private static final short[] majorVersions = { 45, 46, 47, 48 };
private static final short[] minorVersions = { 3, 0, 0, 0 };
/**
* Run the compiler
*/
public synchronized boolean compile(String argv[]) {
String sourcePathArg = null;
String classPathArg = null;
String sysClassPathArg = null;
String extDirsArg = null;
boolean verbosePath = false;
String targetArg = null;
short majorVersion = JAVA_DEFAULT_VERSION;
short minorVersion = JAVA_DEFAULT_MINOR_VERSION;
File destDir = null;
//JCOV
File covFile = null;
String optJcov = "-Xjcov";
String optJcovFile = "-Xjcov:file=";
//end JCOV
int flags = F_WARNINGS | F_DEBUG_LINES | F_DEBUG_SOURCE;
long tm = System.currentTimeMillis();
Vector v = new Vector();
boolean nowrite = false;
String props = null;
String encoding = null;
// These flags are used to make sure conflicting -O and -g
// options aren't given.
String prior_g = null;
String prior_O = null;
exitStatus = EXIT_OK;
// Pre-process command line for @file arguments
try {
argv = CommandLine.parse(argv);
} catch (FileNotFoundException e) {
error("javac.err.cant.read", e.getMessage());
System.exit(1);
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
// Parse arguments
for (int i = 0 ; i < argv.length ; i++) {
if (argv[i].equals("-g")) {
if (prior_g!=null && !(prior_g.equals("-g")))
error("main.conflicting.options", prior_g, "-g");
prior_g = "-g";
flags |= F_DEBUG_LINES;
flags |= F_DEBUG_VARS;
flags |= F_DEBUG_SOURCE;
} else if (argv[i].equals("-g:none")) {
if (prior_g!=null && !(prior_g.equals("-g:none")))
error("main.conflicting.options", prior_g, "-g:none");
prior_g = "-g:none";
flags &= ~F_DEBUG_LINES;
flags &= ~F_DEBUG_VARS;
flags &= ~F_DEBUG_SOURCE;
} else if (argv[i].startsWith("-g:")) {
// We choose to have debugging options conflict even
// if they amount to the same thing (for example,
// -g:source,lines and -g:lines,source). However, multiple
// debugging options are allowed if they are textually
// identical.
if (prior_g!=null && !(prior_g.equals(argv[i])))
error("main.conflicting.options", prior_g, argv[i]);
prior_g = argv[i];
String args = argv[i].substring("-g:".length());
flags &= ~F_DEBUG_LINES;
flags &= ~F_DEBUG_VARS;
flags &= ~F_DEBUG_SOURCE;
while (true) {
if (args.startsWith("lines")) {
flags |= F_DEBUG_LINES;
args = args.substring("lines".length());
} else if (args.startsWith("vars")) {
flags |= F_DEBUG_VARS;
args = args.substring("vars".length());
} else if (args.startsWith("source")) {
flags |= F_DEBUG_SOURCE;
args = args.substring("source".length());
} else {
error("main.bad.debug.option",argv[i]);
usage_error();
return false; // Stop processing now
}
if (args.length() == 0) break;
if (args.startsWith(","))
args = args.substring(",".length());
}
} else if (argv[i].equals("-O")) {
// -O is accepted for backward compatibility, but
// is no longer effective. Use the undocumented
// -XO option to get the old behavior.
if (prior_O!=null && !(prior_O.equals("-O")))
error("main.conflicting.options", prior_O, "-O");
prior_O = "-O";
} else if (argv[i].equals("-nowarn")) {
flags &= ~F_WARNINGS;
} else if (argv[i].equals("-deprecation")) {
flags |= F_DEPRECATION;
} else if (argv[i].equals("-verbose")) {
flags |= F_VERBOSE;
} else if (argv[i].equals("-nowrite")) {
nowrite = true;
} else if (argv[i].equals("-classpath")) {
if ((i + 1) < argv.length) {
if (classPathArg!=null) {
error("main.option.already.seen","-classpath");
}
classPathArg = argv[++i];
} else {
error("main.option.requires.argument","-classpath");
usage_error();
return false; // Stop processing now
}
} else if (argv[i].equals("-sourcepath")) {
if ((i + 1) < argv.length) {
if (sourcePathArg != null) {
error("main.option.already.seen","-sourcepath");
}
sourcePathArg = argv[++i];
} else {
error("main.option.requires.argument","-sourcepath");
usage_error();
return false; // Stop processing now
}
} else if (argv[i].equals("-sysclasspath")) {
if ((i + 1) < argv.length) {
if (sysClassPathArg != null) {
error("main.option.already.seen","-sysclasspath");
}
sysClassPathArg = argv[++i];
} else {
error("main.option.requires.argument","-sysclasspath");
usage_error();
return false; // Stop processing now
}
} else if (argv[i].equals("-bootclasspath")) {
if ((i + 1) < argv.length) {
if (sysClassPathArg != null) {
error("main.option.already.seen","-bootclasspath");
}
sysClassPathArg = argv[++i];
} else {
error("main.option.requires.argument","-bootclasspath");
usage_error();
return false; // Stop processing now
}
} else if (argv[i].equals("-extdirs")) {
if ((i + 1) < argv.length) {
if (extDirsArg != null) {
error("main.option.already.seen","-extdirs");
}
extDirsArg = argv[++i];
} else {
error("main.option.requires.argument","-extdirs");
usage_error();
return false; // Stop processing now
}
} else if (argv[i].equals("-encoding")) {
if ((i + 1) < argv.length) {
if (encoding!=null)
error("main.option.already.seen","-encoding");
encoding = argv[++i];
} else {
error("main.option.requires.argument","-encoding");
usage_error();
return false; // Stop processing now
}
} else if (argv[i].equals("-target")) {
if ((i + 1) < argv.length) {
if (targetArg!=null)
error("main.option.already.seen","-target");
targetArg = argv[++i];
int j;
for (j=0; j<releases.length; j++) {
if (releases[j].equals(targetArg)) {
majorVersion = majorVersions[j];
minorVersion = minorVersions[j];
break;
}
}
if (j==releases.length) {
error("main.unknown.release",targetArg);
usage_error();
return false; // Stop processing now
}
} else {
error("main.option.requires.argument","-target");
usage_error();
return false; // Stop processing now
}
} else if (argv[i].equals("-d")) {
if ((i + 1) < argv.length) {
if (destDir!=null)
error("main.option.already.seen","-d");
destDir = new File(argv[++i]);
if (!destDir.exists()) {
error("main.no.such.directory",destDir.getPath());
usage_error();
return false; // Stop processing now
}
} else {
error("main.option.requires.argument","-d");
usage_error();
return false; // Stop processing now
}
// JCOV
} else if (argv[i].equals(optJcov)) {
flags |= F_COVERAGE;
flags &= ~F_OPT;
flags &= ~F_OPT_INTERCLASS;
} else if ((argv[i].startsWith(optJcovFile)) &&
(argv[i].length() > optJcovFile.length())) {
covFile = new File(argv[i].substring(optJcovFile.length()));
flags &= ~F_OPT;
flags &= ~F_OPT_INTERCLASS;
flags |= F_COVERAGE;
flags |= F_COVDATA;
// end JCOV
} else if (argv[i].equals("-XO")) {
// This is what -O used to be. Now undocumented.
if (prior_O!=null && !(prior_O.equals("-XO")))
error("main.conflicting.options", prior_O, "-XO");
prior_O = "-XO";
flags |= F_OPT;
} else if (argv[i].equals("-Xinterclass")) {
if (prior_O!=null && !(prior_O.equals("-Xinterclass")))
error("main.conflicting.options", prior_O, "-Xinterclass");
prior_O = "-Xinterclass";
flags |= F_OPT;
flags |= F_OPT_INTERCLASS;
flags |= F_DEPENDENCIES;
} else if (argv[i].equals("-Xdepend")) {
flags |= F_DEPENDENCIES;
} else if (argv[i].equals("-Xdebug")) {
flags |= F_DUMP;
// Unadvertised option used by JWS. The non-X version should
// be removed, but we'll leave it in until we find out for
// sure that no one still depends on that option syntax.
} else if (argv[i].equals("-xdepend") || argv[i].equals("-Xjws")) {
flags |= F_PRINT_DEPENDENCIES;
// change the default output in this case:
if (out == System.err) {
out = System.out;
}
} else if (argv[i].equals("-Xstrictdefault")) {
// Make strict floating point the default
flags |= F_STRICTDEFAULT;
} else if (argv[i].equals("-Xverbosepath")) {
verbosePath = true;
} else if (argv[i].equals("-Xstdout")) {
out = System.out;
} else if (argv[i].equals("-X")) {
error("main.unsupported.usage");
return false; // Stop processing now
} else if (argv[i].equals("-Xversion1.2")) {
// Inform the compiler that it need not target VMs
// earlier than version 1.2. This option is here
// for testing purposes only. It is deliberately
// kept orthogonal to the -target option in 1.2.0
// for the sake of stability. These options will
// be merged in a future release.
flags |= F_VERSION12;
} else if (argv[i].endsWith(".java")) {
v.addElement(argv[i]);
} else {
error("main.no.such.option",argv[i]);
usage_error();
return false; // Stop processing now
}
}
if (v.size() == 0 || exitStatus == EXIT_CMDERR) {
usage_error();
return false;
}
// Create our Environment.
BatchEnvironment env = BatchEnvironment.create(out,
sourcePathArg,
classPathArg,
sysClassPathArg,
extDirsArg);
if (verbosePath) {
output(getText("main.path.msg",
env.sourcePath.toString(),
env.binaryPath.toString()));
}
env.flags |= flags;
env.majorVersion = majorVersion;
env.minorVersion = minorVersion;
// JCOV
env.covFile = covFile;
// end JCOV
env.setCharacterEncoding(encoding);
// Preload the "out of memory" error string just in case we run
// out of memory during the compile.
String noMemoryErrorString = getText("main.no.memory");
String stackOverflowErrorString = getText("main.stack.overflow");
env.error(0, "warn.class.is.deprecated", "sun.tools.javac.Main");
try {
// Parse all input files
for (Enumeration e = v.elements() ; e.hasMoreElements() ;) {
File file = new File((String)e.nextElement());
try {
env.parseFile(new ClassFile(file));
} catch (FileNotFoundException ee) {
env.error(0, "cant.read", file.getPath());
exitStatus = EXIT_CMDERR;
}
}
// Do a post-read check on all newly-parsed classes,
// after they have all been read.
for (Enumeration e = env.getClasses() ; e.hasMoreElements() ; ) {
ClassDeclaration c = (ClassDeclaration)e.nextElement();
if (c.getStatus() == CS_PARSED) {
if (c.getClassDefinition().isLocal())
continue;
try {
c.getClassDefinition(env);
} catch (ClassNotFound ee) {
}
}
}
// compile all classes that need compilation
ByteArrayOutputStream buf = new ByteArrayOutputStream(4096);
boolean done;
do {
done = true;
env.flushErrors();
for (Enumeration e = env.getClasses() ; e.hasMoreElements() ; ) {
ClassDeclaration c = (ClassDeclaration)e.nextElement();
SourceClass src;
switch (c.getStatus()) {
case CS_UNDEFINED:
if (!env.dependencies()) {
break;
}
// fall through
case CS_SOURCE:
if (tracing)
env.dtEvent("Main.compile (SOURCE): loading, " + c);
done = false;
env.loadDefinition(c);
if (c.getStatus() != CS_PARSED) {
if (tracing)
env.dtEvent("Main.compile (SOURCE): not parsed, " + c);
break;
}
// fall through
case CS_PARSED:
if (c.getClassDefinition().isInsideLocal()) {
// the enclosing block will check this one
if (tracing)
env.dtEvent("Main.compile (PARSED): skipping local class, " + c);
continue;
}
done = false;
if (tracing) env.dtEvent("Main.compile (PARSED): checking, " + c);
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()) {
if (tracing)
env.dtEvent("Main.compile (CHECKED): bailing out on error, " + c);
c.setDefinition(src, CS_COMPILED);
break;
}
done = false;
buf.reset();
if (tracing)
env.dtEvent("Main.compile (CHECKED): compiling, " + c);
src.compile(buf);
c.setDefinition(src, CS_COMPILED);
src.cleanup(env);
if (src.getNestError() || nowrite) {
continue;
}
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());
exitStatus = EXIT_CMDERR;
continue;
}
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("main.wrote", file.getPath()));
}
} catch (IOException ee) {
env.error(0, "cant.write", file.getPath());
exitStatus = EXIT_CMDERR;
}
// Print class dependencies if requested (-xdepend)
if (env.print_dependencies()) {
src.printClassDependencies(env);
}
}
}
} while (!done);
} catch (OutOfMemoryError ee) {
// The compiler has run out of memory. Use the error string
// which we preloaded.
env.output(noMemoryErrorString);
exitStatus = EXIT_SYSERR;
return false;
} catch (StackOverflowError ee) {
env.output(stackOverflowErrorString);
exitStatus = EXIT_SYSERR;
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()) {
ee.printStackTrace();
env.error(0, "fatal.error");
exitStatus = EXIT_ABNORMAL;
}
} catch (Exception ee) {
if (env.nerrors == 0 || env.dump()) {
ee.printStackTrace();
env.error(0, "fatal.exception");
exitStatus = EXIT_ABNORMAL;
}
}
int ndepfiles = env.deprecationFiles.size();
if (ndepfiles > 0 && env.warnings()) {
int ndeps = env.ndeprecations;
Object file1 = env.deprecationFiles.elementAt(0);
if (env.deprecation()) {
if (ndepfiles > 1) {
env.error(0, "warn.note.deprecations",
new Integer(ndepfiles), new Integer(ndeps));
} else {
env.error(0, "warn.note.1deprecation",
file1, new Integer(ndeps));
}
} else {
if (ndepfiles > 1) {
env.error(0, "warn.note.deprecations.silent",
new Integer(ndepfiles), new Integer(ndeps));
} else {
env.error(0, "warn.note.1deprecation.silent",
file1, new Integer(ndeps));
}
}
}
env.flushErrors();
env.shutdown();
boolean status = true;
if (env.nerrors > 0) {
String msg = "";
if (env.nerrors > 1) {
msg = getText("main.errors", env.nerrors);
} else {
msg = getText("main.1error");
}
if (env.nwarnings > 0) {
if (env.nwarnings > 1) {
msg += ", " + getText("main.warnings", env.nwarnings);
} else {
msg += ", " + getText("main.1warning");
}
}
output(msg);
if (exitStatus == EXIT_OK) {
// Allow EXIT_CMDERR or EXIT_ABNORMAL to take precedence.
exitStatus = EXIT_ERROR;
}
status = false;
} else {
if (env.nwarnings > 0) {
if (env.nwarnings > 1) {
output(getText("main.warnings", env.nwarnings));
} else {
output(getText("main.1warning"));
}
}
}
//JCOV
if (env.covdata()) {
Assembler CovAsm = new Assembler();
CovAsm.GenJCov(env);
}
// end JCOV
// We're done
if (env.verbose()) {
tm = System.currentTimeMillis() - tm;
output(getText("main.done_in", Long.toString(tm)));
}
return status;
}
/**
* Main program
*/
public static void main(String argv[]) {
OutputStream out = System.err;
// This is superceeded by the -Xstdout option, but we leave
// in the old property check for compatibility.
if (Boolean.getBoolean("javac.pipe.output")) {
out = System.out;
}
Main compiler = new Main(out, "javac");
System.exit(compiler.compile(argv) ? 0 : compiler.exitStatus);
}
}