package polyglot.main;
import polyglot.frontend.Compiler;
import polyglot.frontend.ExtensionInfo;
import polyglot.util.ErrorInfo;
import polyglot.util.ErrorQueue;
import polyglot.util.StdErrorQueue;
import polyglot.util.QuotedStringTokenizer;
import java.io.*;
import java.util.*;
/** Main is the main program of the extensible compiler. It should not
* need to be replaced.
*/
public class Main
{
/** Source files specified on the command line */
private Set source;
//ak333: set to 'public' so cppout MakePass can see it.
public final static String verbose = "verbose";
/* modifies args */
protected ExtensionInfo getExtensionInfo(List args) throws TerminationException {
ExtensionInfo ext = null;
for (Iterator i = args.iterator(); i.hasNext(); ) {
String s = (String)i.next();
if (s.equals("-ext") || s.equals("-extension"))
{
if (ext != null) {
throw new TerminationException("only one extension can be specified");
}
i.remove();
if (!i.hasNext()) {
throw new TerminationException("missing argument");
}
String extName = (String)i.next();
i.remove();
ext = loadExtension("polyglot.ext." + extName + ".ExtensionInfo");
}
else if (s.equals("-extclass"))
{
if (ext != null) {
throw new TerminationException("only one extension can be specified");
}
i.remove();
if (!i.hasNext()) {
throw new TerminationException("missing argument");
}
String extClass = (String)i.next();
i.remove();
ext = loadExtension(extClass);
}
}
if (ext != null) {
return ext;
}
return loadExtension("polyglot.ext.jl.ExtensionInfo");
}
public void start(String[] argv) throws TerminationException {
start(argv, null);
}
public void start(String[] argv, ErrorQueue eq) throws TerminationException {
source = new LinkedHashSet();
List args = explodeOptions(argv);
ExtensionInfo ext = getExtensionInfo(args);
Options options = ext.getOptions();
// Allow all objects to get access to the Options object. This hack should
// be fixed somehow. XXX###@@@
Options.global = options;
try {
argv = (String[]) args.toArray(new String[0]);
options.parseCommandLine(argv, source);
}
catch (UsageError ue) {
PrintStream out = (ue.exitCode==0 ? System.out : System.err);
if (ue.getMessage() != null && ue.getMessage().length() > 0) {
out.println(ext.compilerName() +": " + ue.getMessage());
}
options.usage(out);
throw new TerminationException(ue.exitCode);
}
if (eq == null) {
eq = new StdErrorQueue(System.err,
options.error_count,
ext.compilerName());
}
Compiler compiler = new Compiler(ext, eq);
long time0 = System.currentTimeMillis();
if (!compiler.compile(source)) {
throw new TerminationException(1);
}
if (Report.should_report(verbose, 1))
Report.report(1, "Output files: " + compiler.outputFiles());
long start_time = System.currentTimeMillis();
/* Now call javac or jikes, if necessary. */
if (!invokePostCompiler(options, compiler, eq)) {
throw new TerminationException(1);
}
if (Report.should_report(verbose, 1)) {
reportTime("Finished compiling Java output files. time=" +
(System.currentTimeMillis() - start_time), 1);
reportTime("Total time=" + (System.currentTimeMillis() - time0), 1);
}
}
protected boolean invokePostCompiler(Options options,
Compiler compiler,
ErrorQueue eq) {
if (options.post_compiler != null && !options.output_stdout) {
Runtime runtime = Runtime.getRuntime();
QuotedStringTokenizer st = new QuotedStringTokenizer(options.post_compiler);
int pc_size = st.countTokens();
String[] javacCmd = new String[pc_size+2+compiler.outputFiles().size()];
int j = 0;
for (int i = 0; i < pc_size; i++) {
javacCmd[j++] = st.nextToken();
}
javacCmd[j++] = "-classpath";
javacCmd[j++] = options.constructPostCompilerClasspath();
Iterator iter = compiler.outputFiles().iterator();
for (; iter.hasNext(); j++) {
javacCmd[j] = (String) iter.next();
}
if (Report.should_report(verbose, 1)) {
StringBuffer cmdStr = new StringBuffer();
for (int i = 0; i < javacCmd.length; i++)
cmdStr.append(javacCmd[i]+" ");
Report.report(1, "Executing post-compiler " + cmdStr);
}
try {
Process proc = runtime.exec(javacCmd);
InputStreamReader err = null;
try {
err = new InputStreamReader(proc.getErrorStream());
char[] c = new char[72];
int len;
StringBuffer sb = new StringBuffer();
while((len = err.read(c)) > 0) {
sb.append(String.valueOf(c, 0, len));
}
if (sb.length() != 0) {
eq.enqueue(ErrorInfo.POST_COMPILER_ERROR, sb.toString());
}
}
finally {
err.close();
}
proc.waitFor();
if (!options.keep_output_files) {
String[] rmCmd = new String[1+compiler.outputFiles().size()];
rmCmd[0] = "rm";
for (int i = 1; i < rmCmd.length; i++)
rmCmd[i] = javacCmd[i+2];
runtime.exec(rmCmd);
}
if (proc.exitValue() > 0) {
eq.enqueue(ErrorInfo.POST_COMPILER_ERROR,
"Non-zero return code: " + proc.exitValue());
return false;
}
}
catch(Exception e) {
eq.enqueue(ErrorInfo.POST_COMPILER_ERROR, e.getMessage());
return false;
}
}
return true;
}
private List explodeOptions(String[] args) throws TerminationException {
LinkedList ll = new LinkedList();
for (int i = 0; i < args.length; i++) {
// special case for the @ command-line parameter
if (args[i].startsWith("@")) {
String fn = args[i].substring(1);
try {
BufferedReader lr = new BufferedReader(new FileReader(fn));
LinkedList newArgs = new LinkedList();
while (true) {
String l = lr.readLine();
if (l == null)
break;
StringTokenizer st = new StringTokenizer(l, " ");
while (st.hasMoreTokens())
newArgs.add(st.nextToken());
}
lr.close();
ll.addAll(newArgs);
}
catch (java.io.IOException e) {
throw new TerminationException("cmdline parser: couldn't read args file "+fn);
}
continue;
}
ll.add(args[i]);
}
return ll;
}
public static final void main(String args[]) {
try {
new Main().start(args);
}
catch (TerminationException te) {
if (te.getMessage() != null)
(te.exitCode==0?System.out:System.err).println(te.getMessage());
System.exit(te.exitCode);
}
}
static final ExtensionInfo loadExtension(String ext) throws TerminationException {
if (ext != null && ! ext.equals("")) {
Class extClass = null;
try {
extClass = Class.forName(ext);
}
catch (ClassNotFoundException e) {
throw new TerminationException(
"Extension " + ext +
" not found: could not find class " + ext + ".");
}
try {
return (ExtensionInfo) extClass.newInstance();
}
catch (ClassCastException e) {
throw new TerminationException(
ext + " is not a valid polyglot extension:" +
" extension class " + ext +
" exists but is not a subclass of ExtensionInfo");
}
catch (Exception e) {
throw new TerminationException(
"Extension " + ext +
" could not be loaded: could not instantiate " + ext + ".");
}
}
return null;
}
static private Collection timeTopics = new ArrayList(1);
static {
timeTopics.add("time");
}
static private void reportTime(String msg, int level) {
Report.report(level, msg);
}
/**
* This exception signals termination of the compiler. It should be used
* instead of <code>System.exit</code> to allow Polyglot to be called
* within a JVM that wasn't started specifically for Polyglot, e.g. the
* Apache ANT framework.
*/
public static class TerminationException extends RuntimeException {
final public int exitCode;
public TerminationException(String msg) {
this(msg, 1);
}
public TerminationException(int exit) {
this.exitCode = exit;
}
public TerminationException(String msg, int exit) {
super(msg);
this.exitCode = exit;
}
}
}