diff --git a/app/.classpath b/app/.classpath index 8cd0ca450..ea4256063 100644 --- a/app/.classpath +++ b/app/.classpath @@ -13,5 +13,6 @@ + diff --git a/app/src/processing/app/debug/Compiler.java b/app/src/processing/app/debug/Compiler.java index 8c396b535..8f45f5f02 100644 --- a/app/src/processing/app/debug/Compiler.java +++ b/app/src/processing/app/debug/Compiler.java @@ -29,37 +29,42 @@ import processing.app.SketchCode; import processing.core.*; import java.io.*; +//import java.text.MessageFormat; +//import java.text.ParseException; +//import java.text.ParsePosition; import java.util.*; import java.util.zip.*; +import org.eclipse.jdt.core.compiler.CompilationProgress; + public class Compiler { - private static final String javacBundleName = - "com.sun.tools.javac.resources.javac"; - private static ResourceBundle javacResources; - - private static final String compilerBundleName = - "com.sun.tools.javac.resources.compiler"; - private static ResourceBundle compilerResources; +// private static final String javacBundleName = +// "com.sun.tools.javac.resources.javac"; +// private static ResourceBundle javacResources; +// +// private static final String compilerBundleName = +// "com.sun.tools.javac.resources.compiler"; +// private static ResourceBundle compilerResources; public Compiler() { - try { - javacResources = ResourceBundle.getBundle(javacBundleName); - compilerResources = ResourceBundle.getBundle(compilerBundleName); - - } catch (MissingResourceException e) { - e.printStackTrace(); - //throw new Error("Resources for javac could not be found."); - throw new RuntimeException("The JDK is not installed properly. " + - "Please use the version of Processing " + - "that includes Java."); - } +// try { +// javacResources = ResourceBundle.getBundle(javacBundleName); +// compilerResources = ResourceBundle.getBundle(compilerBundleName); +// +// } catch (MissingResourceException e) { +// e.printStackTrace(); +// //throw new Error("Resources for javac could not be found."); +// throw new RuntimeException("The JDK is not installed properly. " + +// "Please use the version of Processing " + +// "that includes Java."); +// } } /** - * Fire up 'ole javac based on this interface. + * Compile with ECJ. * * @param sketch Sketch object to be compiled. * @param buildPath Where the temporary files live and will be built from. @@ -70,12 +75,15 @@ public class Compiler { String buildPath) throws RunnerException { // This will be filled in if anyone gets angry RunnerException exception = null; - + boolean success = false; + String baseCommand[] = new String[] { + "-Xemacs", + //"-noExit", // not necessary for ecj "-source", "1.5", "-target", "1.5", "-classpath", sketch.getClassPath(), - "-nowarn", // we're not currently interested in warnings (ignored?) + "-nowarn", // we're not currently interested in warnings (works in ecj) "-d", buildPath // output the classes in the buildPath }; //PApplet.println(baseCommand); @@ -98,7 +106,7 @@ public class Compiler { } //PApplet.println(command); - int result = -1; // needs to be set bad by default, in case hits IOE below +// int result = -1; // needs to be set bad by default, in case hits IOE below try { // Load errors into a local StringBuffer @@ -117,8 +125,15 @@ public class Compiler { // Wrap as a PrintWriter since that's what compile() wants PrintWriter writer = new PrintWriter(internalWriter); - result = com.sun.tools.javac.Main.compile(command, writer); - + //result = com.sun.tools.javac.Main.compile(command, writer); + + CompilationProgress progress = null; + PrintWriter outWriter = new PrintWriter(System.out); + success = + org.eclipse.jdt.core.compiler.batch.BatchCompiler.compile(command, + outWriter, + writer, + progress); // Close out the stream for good measure writer.flush(); writer.close(); @@ -131,61 +146,38 @@ public class Compiler { while ((line = reader.readLine()) != null) { //System.out.println("got line " + line); // debug - /* -compiler.misc.count.error=\ - {0} error -compiler.misc.count.error.plural=\ - {0} errors -compiler.misc.count.warn=\ - {0} warning -compiler.misc.count.warn.plural=\ - {0} warnings - */ - // Check to see if this is the last line. - if ((PApplet.match(line, "\\d+ error[s]?") != null) || - (PApplet.match(line, "\\d+ warning[s]?") != null)) { - break; - } - - // Hide these because people are getting confused - // http://dev.processing.org/bugs/show_bug.cgi?id=817 - // com/sun/tools/javac/resources/compiler.properties - //if (line.startsWith("Note: ")) { - String compilerNote = compilerResources.getString("compiler.note.note"); - if (line.startsWith(compilerNote)) { - //System.out.println("found a compiler note"); - - // if you mention serialVersionUID one more time, i'm kickin' you out - if (line.indexOf("serialVersionUID") != -1) continue; - // {0} uses unchecked or unsafe operations. - // Some input files use unchecked or unsafe operations. - if (line.indexOf("or unsafe operations") != -1) continue; - // {0} uses or overrides a deprecated API. - // Some input files use or override a deprecated API. - if (line.indexOf("or override") != -1) continue; - // Recompile with -Xlint:deprecation for details. - // Recompile with -Xlint:unchecked for details. - if (line.indexOf("Recompile with -Xlint:") != -1) continue; - System.err.println(line); - continue; - } - - String errorFormat = "([\\w\\d_]+.java):(\\d+):\\s*(.*)\\s*"; + // get first line, which contains file name, line number, + // and at least the first line of the error message + String errorFormat = "([\\w\\d_]+.java):(\\d+):\\s*(.*):\\s*(.*)\\s*"; String[] pieces = PApplet.match(line, errorFormat); + + // if it's something unexpected, die and print the mess to the console if (pieces == null) { exception = new RunnerException("Cannot parse error text: " + line); exception.hideStackTrace(); // Send out the rest of the error message to the console. System.err.println(line); while ((line = reader.readLine()) != null) { +// for (int i = lineIndex; i < lineCount; i++) { +// System.err.println(lines[i]); +// } System.err.println(line); } break; } + + // pieces[2] will contain "error" or "warning" (others?) + if (pieces[2].equals("warning")) { + System.out.println("nah, nevermind: " + pieces[3]); + continue; + } + + // translate the java filename and line number into a un-preprocessed + // location inside a source file or tab in the environment. String dotJavaFilename = pieces[0]; // Line numbers are 1-indexed from javac int dotJavaLineIndex = PApplet.parseInt(pieces[1]) - 1; - String errorMessage = pieces[2]; + String errorMessage = pieces[3]; int codeIndex = -1; int codeLine = -1; @@ -218,6 +210,7 @@ compiler.misc.count.warn.plural=\ // for a test case once message parsing is implemented, // use new Font(...) since that wasn't getting picked up properly. + /* if (errorMessage.equals("cannot find symbol")) { handleCannotFindSymbol(reader, exception); @@ -236,6 +229,77 @@ compiler.misc.count.warn.plural=\ } else { exception = new RunnerException(errorMessage); } + */ + if (errorMessage.startsWith("The import ") && + errorMessage.endsWith("cannot be resolved")) { + // The import poo cannot be resolved + //import poo.shoe.blah.*; + String what = errorMessage.substring("The import ".length()); + what = what.substring(0, what.indexOf(' ')); + exception.setMessage("The package " + + "\u201C" + what + "\u201D" + + " does not exist. " + + "You might be missing a library."); + + } else if (errorMessage.endsWith("cannot be resolved to a type")) { + // xxx cannot be resolved to a type + //xxx c; + + String what = errorMessage.substring(0, errorMessage.indexOf(' ')); + + if (what.equals("BFont") || + what.equals("BGraphics") || + what.equals("BImage")) { + handleCrustyCode(exception); + + } else { + exception.setMessage("Cannot find a class or type " + + "named \u201C" + what + "\u201D"); + } + + } else if (errorMessage.endsWith("cannot be resolved")) { + // xxx cannot be resolved + //println(xxx); + + String what = errorMessage.substring(0, errorMessage.indexOf(' ')); + + if (what.equals("LINE_LOOP") || + what.equals("LINE_STRIP") || + what.equals("framerate")) { + handleCrustyCode(exception); + + } else { + exception.setMessage("Cannot find anything " + + "named \u201C" + what + "\u201D"); + } + + } else if (errorMessage.startsWith("Duplicate")) { + // "Duplicate nested type xxx" + // "Duplicate local variable xxx" + + } else { + String[] parts = null; + + // The method xxx(String) is undefined for the type Temporary_XXXX_XXXX + //xxx("blah"); + // The method xxx(String, int) is undefined for the type Temporary_XXXX_XXXX + //xxx("blah", 34); + // The method xxx(String, int) is undefined for the type PApplet + //PApplet.sub("ding"); + String undefined = + "The method (\\S+\\(.*\\)) is undefined for the type (.*)"; + parts = PApplet.match(errorMessage, undefined); + if (parts != null) { + if (parts[0].equals("framerate(int)") || + parts[0].equals("push()")) { + handleCrustyCode(exception); + } else { + String mess = "The function " + parts[0] + " does not exist."; + exception.setMessage(mess); + } + break; + } + } if (exception != null) { // The stack trace just shows that this happened inside the compiler, // which is a red herring. Don't ever show it for compiler stuff. @@ -247,96 +311,356 @@ compiler.misc.count.warn.plural=\ String bigSigh = "Error while compiling. (" + e.getMessage() + ")"; exception = new RunnerException(bigSigh); e.printStackTrace(); - result = 1; + success = false; } // In case there was something else. if (exception != null) throw exception; - // Success means that 'result' is set to zero - return (result == 0); + return success; } + + + /** + * Fire up 'ole javac based on this interface. + * + * @param sketch Sketch object to be compiled. + * @param buildPath Where the temporary files live and will be built from. + * @return + * @throws RunnerException Only if there's a problem. Only then. + */ +// public boolean compileJavac(Sketch sketch, +// String buildPath) throws RunnerException { +// // This will be filled in if anyone gets angry +// RunnerException exception = null; +// +// String baseCommand[] = new String[] { +// "-source", "1.5", +// "-target", "1.5", +// "-classpath", sketch.getClassPath(), +// "-nowarn", // we're not currently interested in warnings (ignored?) +// "-d", buildPath // output the classes in the buildPath +// }; +// //PApplet.println(baseCommand); +// +// // make list of code files that need to be compiled +// // (some files are skipped if they contain no class) +// String[] preprocNames = new String[sketch.getCodeCount()]; +// int preprocCount = 0; +// for (int i = 0; i < sketch.getCodeCount(); i++) { +// if (sketch.getCode(i).preprocName != null) { +// preprocNames[preprocCount++] = sketch.getCode(i).preprocName; +// } +// } +// String[] command = new String[baseCommand.length + preprocCount]; +// System.arraycopy(baseCommand, 0, command, 0, baseCommand.length); +// // append each of the files to the command string +// for (int i = 0; i < preprocCount; i++) { +// command[baseCommand.length + i] = +// buildPath + File.separator + preprocNames[i]; +// } +// //PApplet.println(command); +// +// int result = -1; // needs to be set bad by default, in case hits IOE below +// +// try { +// // Load errors into a local StringBuffer +// final StringBuffer errorBuffer = new StringBuffer(); +// +// // Create single method dummy writer class to slurp errors from javac +// Writer internalWriter = new Writer() { +// public void write(char[] buf, int off, int len) { +// errorBuffer.append(buf, off, len); +// } +// +// public void flush() { } +// +// public void close() { } +// }; +// // Wrap as a PrintWriter since that's what compile() wants +// PrintWriter writer = new PrintWriter(internalWriter); +// +// result = com.sun.tools.javac.Main.compile(command, writer); +// +// // Close out the stream for good measure +// writer.flush(); +// writer.close(); +// +//// BufferedReader reader = +//// new BufferedReader(new StringReader(errorBuffer.toString())); +// //System.err.println(errorBuffer.toString()); +// +//// String m = errorBuffer.toString(); +// //ParsePosition mp = new ParsePosition(0); +// +//// while (mp.getIndex() < m.length()) { // reading messages +// String line = null; +// int lineIndex = 0; +// String[] lines = PApplet.split(errorBuffer.toString(), '\n'); +// int lineCount = lines.length; +// while (lineIndex < lineCount) { +// //while ((line = reader.readLine()) != null) { +// //System.out.println("got line " + line); // debug +// +// /* +//compiler.misc.count.error=\ +// {0} error +//compiler.misc.count.error.plural=\ +// {0} errors +//compiler.misc.count.warn=\ +// {0} warning +//compiler.misc.count.warn.plural=\ +// {0} warnings +// */ +// // Check to see if this is the last line. +//// if ((PApplet.match(line, "\\d+ error[s]?") != null) || +//// (PApplet.match(line, "\\d+ warning[s]?") != null)) { +//// break; +//// } +// if (isCompilerMatch(line, "compiler.misc.count.error") || +// isCompilerMatch(line, "compiler.misc.count.error.plural") || +// isCompilerMatch(line, "compiler.misc.count.warn") || +// isCompilerMatch(line, "compiler.misc.count.warn.plural")) { +// break; +// } +// +// // Hide these because people are getting confused +// // http://dev.processing.org/bugs/show_bug.cgi?id=817 +// // com/sun/tools/javac/resources/compiler.properties +// //if (line.startsWith("Note: ")) { +// String compilerNote = compilerResources.getString("compiler.note.note"); +// MessageFormat noteFormat = new MessageFormat(compilerNote + " {0}"); +// Object[] noteFound; +// try { +// noteFound = noteFormat.parse(line); +// if (noteFound != null) { +// System.out.println("gefunden " + noteFound[0]); +// +// /* +// // if you mention serialVersionUID one more time, i'm kickin' you out +// if (line.indexOf("serialVersionUID") != -1) continue; +// // {0} uses unchecked or unsafe operations. +// // Some input files use unchecked or unsafe operations. +// if (line.indexOf("or unsafe operations") != -1) continue; +// // {0} uses or overrides a deprecated API. +// // Some input files use or override a deprecated API. +// if (line.indexOf("or override") != -1) continue; +// // Recompile with -Xlint:deprecation for details. +// // Recompile with -Xlint:unchecked for details. +// if (line.indexOf("Recompile with -Xlint:") != -1) continue; +// System.err.println(line); +// */ +// continue; +// } +// } catch (ParseException e) { +// e.printStackTrace(); +// } +// +// // get first line, which contains file name, line number, +// // and at least the first line of the error message +// String errorFormat = "([\\w\\d_]+.java):(\\d+):\\s*(.*)\\s*"; +// String[] pieces = PApplet.match(line, errorFormat); +// +// // if it's something unexpected, die and print the mess to the console +// if (pieces == null) { +// exception = new RunnerException("Cannot parse error text: " + line); +// exception.hideStackTrace(); +// // Send out the rest of the error message to the console. +// System.err.println(line); +// //while ((line = reader.readLine()) != null) { +// for (int i = lineIndex; i < lineCount; i++) { +// System.err.println(lines[i]); +// } +// break; +// } +// +// // translate the java filename and line number into a un-preprocessed +// // location inside a source file or tab in the environment. +// String dotJavaFilename = pieces[0]; +// // Line numbers are 1-indexed from javac +// int dotJavaLineIndex = PApplet.parseInt(pieces[1]) - 1; +// String errorMessage = pieces[2]; +// +// int codeIndex = -1; +// int codeLine = -1; +// for (int i = 0; i < sketch.getCodeCount(); i++) { +// String name = sketch.getCode(i).preprocName; +// if ((name != null) && dotJavaFilename.equals(name)) { +// codeIndex = i; +// } +// } +// //System.out.println("code index/line are " + codeIndex + " " + codeLine); +// //System.out.println("java line number " + dotJavaLineIndex + " from " + dotJavaFilename); +// +// if (codeIndex == 0) { // main class, figure out which tab +// for (int i = 1; i < sketch.getCodeCount(); i++) { +// SketchCode code = sketch.getCode(i); +// +// if (code.flavor == Sketch.PDE) { +// if (code.preprocOffset <= dotJavaLineIndex) { +// codeIndex = i; +// //System.out.println("i'm thinkin file " + i); +// } +// } +// } +// } +// //System.out.println("preproc offset is " + sketch.getCode(codeIndex).preprocOffset); +// codeLine = dotJavaLineIndex - sketch.getCode(codeIndex).preprocOffset; +// //System.out.println("code line now " + codeLine); +// exception = new RunnerException(errorMessage, codeIndex, codeLine, -1, false); +// +// // for a test case once message parsing is implemented, +// // use new Font(...) since that wasn't getting picked up properly. +// +// if (errorMessage.equals("cannot find symbol")) { +// handleCannotFindSymbol(reader, exception); +// +// } else if (errorMessage.indexOf("is already defined") != -1) { +// reader.readLine(); // repeats the line of code w/ error +// int codeColumn = caretColumn(reader.readLine()); +// exception = new RunnerException(errorMessage, +// codeIndex, codeLine, codeColumn); +// +// } else if (errorMessage.startsWith("package") && +// errorMessage.endsWith("does not exist")) { +// // Because imports are stripped out and re-added to the 0th line of +// // the preprocessed code, codeLine will always be wrong for imports. +// exception = new RunnerException("P" + errorMessage.substring(1) + +// ". You might be missing a library."); +// } else { +// exception = new RunnerException(errorMessage); +// } +// if (exception != null) { +// // The stack trace just shows that this happened inside the compiler, +// // which is a red herring. Don't ever show it for compiler stuff. +// exception.hideStackTrace(); +// break; +// } +// } +// } catch (IOException e) { +// String bigSigh = "Error while compiling. (" + e.getMessage() + ")"; +// exception = new RunnerException(bigSigh); +// e.printStackTrace(); +// result = 1; +// } +// // In case there was something else. +// if (exception != null) throw exception; +// +// // Success means that 'result' is set to zero +// return (result == 0); +// } +// +// +// boolean isCompilerMatch(String line, String format) { +// return compilerMatch(line, format) != null; +// } +// +// +// Object[] compilerMatch(String line, String name) { +// String format = compilerResources.getString(name); +// MessageFormat mf = new MessageFormat(format); +// Object[] matches = null; +// try { +// matches = mf.parse(line); +// } catch (ParseException e) { +// e.printStackTrace(); +// } +// return matches; +// } + - +// boolean isCompilerMatch(String line, ParsePosition pos, String format) { +// return compilerMatch(line, pos, format) != null; +// } +// +// +// Object[] compilerMatch(String line, ParsePosition pos, String name) { +// String format = compilerResources.getString(name); +// MessageFormat mf = new MessageFormat(format); +// Object[] matches = mf.parse(line, pos); +// return matches; +// } + + // Tell-tale signs of old code copied and pasted from the web. // Detect classes BFont, BGraphics, BImage; methods framerate, push; // and variables LINE_LOOP and LINE_STRIP. - static HashMap crusties = new HashMap(); - static { - crusties.put("BFont", new Object()); - crusties.put("BGraphics", new Object()); - crusties.put("BImage", new Object()); - crusties.put("framerate", new Object()); - crusties.put("push", new Object()); - crusties.put("LINE_LOOP", new Object()); - crusties.put("LINE_STRIP", new Object()); - } +// static HashMap crusties = new HashMap(); +// static { +// crusties.put("BFont", new Object()); +// crusties.put("BGraphics", new Object()); +// crusties.put("BImage", new Object()); +// crusties.put("framerate", new Object()); +// crusties.put("push", new Object()); +// crusties.put("LINE_LOOP", new Object()); +// crusties.put("LINE_STRIP", new Object()); +// } - void handleCannotFindSymbol(BufferedReader reader, - RunnerException rex) throws IOException { - String symbolLine = reader.readLine(); - String locationLine = reader.readLine(); - /*String codeLine =*/ reader.readLine(); - String caretLine = reader.readLine(); - rex.setCodeColumn(caretColumn(caretLine)); - - String[] pieces = - PApplet.match(symbolLine, "symbol\\s*:\\s*(\\w+)\\s+(.*)"); - if (pieces != null) { - if (pieces[0].equals("class") || - pieces[0].equals("variable")) { - rex.setMessage("Cannot find a " + pieces[0] + " " + - "named \u201C" + pieces[1] + "\u201D"); - if (crusties.get(pieces[1]) != null) { - handleCrustyCode(rex); - } - - } else if (pieces[0].equals("method")) { - int leftParen = pieces[1].indexOf("("); - int rightParen = pieces[1].indexOf(")"); - - String methodName = pieces[1].substring(0, leftParen); - String methodParams = pieces[1].substring(leftParen + 1, rightParen); - - String message = - "Cannot find a function named \u201C" + methodName + "\u201D"; - if (methodParams.length() > 0) { - if (methodParams.indexOf(',') != -1) { - message += " with parameters "; - } else { - message += " with parameter "; - } - message += methodParams; - } - - String locationClass = "location: class "; - if (locationLine.startsWith(locationClass) && - // don't include the class name when it's a temp class - locationLine.indexOf("Temporary_") == -1) { - String className = locationLine.substring(locationClass.length()); - // If no dot exists, -1 + 1 is 0, so this will have no effect. - className = className.substring(className.lastIndexOf('.') + 1); - int bracket = className.indexOf('['); - if (bracket == -1) { - message += " in class " + className; - } else { - className = className.substring(0, bracket); - message += " for an array of " + className + " objects"; - } - } - message += "."; - rex.setMessage(message); - - // On second thought, make sure this isn't just some alpha/beta code - if (crusties.get(methodName) != null) { - handleCrustyCode(rex); - } - - } else { - System.out.println(symbolLine); - } - } - } +// void handleCannotFindSymbol(BufferedReader reader, +// RunnerException rex) throws IOException { +// String symbolLine = reader.readLine(); +// String locationLine = reader.readLine(); +// /*String codeLine =*/ reader.readLine(); +// String caretLine = reader.readLine(); +// rex.setCodeColumn(caretColumn(caretLine)); +// +// String[] pieces = +// PApplet.match(symbolLine, "symbol\\s*:\\s*(\\w+)\\s+(.*)"); +// if (pieces != null) { +// if (pieces[0].equals("class") || +// pieces[0].equals("variable")) { +// rex.setMessage("Cannot find a " + pieces[0] + " " + +// "named \u201C" + pieces[1] + "\u201D"); +// if (crusties.get(pieces[1]) != null) { +// handleCrustyCode(rex); +// } +// +// } else if (pieces[0].equals("method")) { +// int leftParen = pieces[1].indexOf("("); +// int rightParen = pieces[1].indexOf(")"); +// +// String methodName = pieces[1].substring(0, leftParen); +// String methodParams = pieces[1].substring(leftParen + 1, rightParen); +// +// String message = +// "Cannot find a function named \u201C" + methodName + "\u201D"; +// if (methodParams.length() > 0) { +// if (methodParams.indexOf(',') != -1) { +// message += " with parameters "; +// } else { +// message += " with parameter "; +// } +// message += methodParams; +// } +// +// String locationClass = "location: class "; +// if (locationLine.startsWith(locationClass) && +// // don't include the class name when it's a temp class +// locationLine.indexOf("Temporary_") == -1) { +// String className = locationLine.substring(locationClass.length()); +// // If no dot exists, -1 + 1 is 0, so this will have no effect. +// className = className.substring(className.lastIndexOf('.') + 1); +// int bracket = className.indexOf('['); +// if (bracket == -1) { +// message += " in class " + className; +// } else { +// className = className.substring(0, bracket); +// message += " for an array of " + className + " objects"; +// } +// } +// message += "."; +// rex.setMessage(message); +// +// // On second thought, make sure this isn't just some alpha/beta code +// if (crusties.get(methodName) != null) { +// handleCrustyCode(rex); +// } +// +// } else { +// System.out.println(symbolLine); +// } +// } +// } void handleCrustyCode(RunnerException rex) {