diff --git a/processing/app/PdeCompiler.java b/processing/app/PdeCompiler.java index cb0b91a9b..6d5b27ee3 100644 --- a/processing/app/PdeCompiler.java +++ b/processing/app/PdeCompiler.java @@ -33,13 +33,15 @@ public class PdeCompiler implements PdeMessageConsumer { static final String SUPER_BADNESS = "Compiler error, please submit this code to " + BUGS_URL; + PdeSketch sketch; + String buildPath; + //String buildPath; //String className; //File includeFolder; PdeException exception; //PdeEditor editor; - /* public PdeCompiler(String buildPath, String className, File includeFolder, PdeEditor editor) { @@ -53,19 +55,22 @@ public class PdeCompiler implements PdeMessageConsumer { public boolean compile(PrintStream leechErr) { */ - public PdeCompiler() { } + public PdeCompiler() { } // consider this a warning, you werkin soon. - public boolean compile(PdeSketch sketch) { - String userdir = System.getProperty("user.dir") + File.separator; + public boolean compile(PdeSketch sketch, String buildPath) { + this.sketch = sketch; + this.buildPath = buildPath; + + // the pms object isn't used for anything but storage + PdeMessageStream pms = new PdeMessageStream(this); - //System.out.println(userdir + "jikes"); - //System.out.println(System.getProperty("sun.boot.class.path")); - - String command[] = new String[] { - // linux doesn't seem to like this - // though windows probably doesn't care - ((PdeBase.platform == PdeBase.MACOSX) ? userdir + "jikes" : "jikes"), + String baseCommand[] = new String[] { + // user.dir is folder containing P5 (and therefore jikes) + // macosx needs the extra path info. linux doesn't like it, though + // windows doesn't seem to care. write once, headache anywhere. + ((PdeBase.platform != PdeBase.MACOSX) ? "jikes" : + System.getProperty("user.dir") + File.separator + "jikes"), // necessary to make output classes compatible with 1.1 // i.e. so that exported applets can work with ms jvm on the web @@ -81,27 +86,29 @@ public class PdeCompiler implements PdeMessageConsumer { // also for windows because qtjava will most likely be here // and for linux, it just doesn't hurt "-classpath", - calcClassPath(includeFolder), + sketch.classPath, //calcClassPath(includeFolder), "-nowarn", // we're not currently interested in warnings "+E", // output errors in machine-parsable format - "-d", buildPath, // output the classes in the buildPath - buildPath + File.separator + className + ".java" // file to compile + "-d", buildPath // output the classes in the buildPath + //buildPath + File.separator + className + ".java" // file to compile }; - //for (int i = 0; i < command.length; i++) { - //System.out.println("C" + i + ": " + command[i]); - //System.out.println(); - //} + String command[] = new String[baseCommand.length + sketch.codeCount]; + System.arraycopy(baseCommand, 0, command, 0, baseCommand.length); + // append each of the files to the command string + for (int i = 0; i < sketch.codeCount; i++) { + command[baseCommand.length + i] = + buildPath + File.separator + sketch.code[i].preprocName; + } firstErrorFound = false; // haven't found any errors yet secondErrorFound = false; int result = 0; // pre-initialized to quiet a bogus warning from jikes try { - - // execute the compiler, and create threads to deal with the input - // and error streams + // execute the compiler, and create threads to deal + // with the input and error streams // Process process = Runtime.getRuntime().exec(command); new PdeMessageSiphon(process.getInputStream(), this); @@ -115,8 +122,7 @@ public class PdeCompiler implements PdeMessageConsumer { try { result = process.waitFor(); compiling = false; - } catch (InterruptedException intExc) { - } + } catch (InterruptedException ignored) { } } } catch (Exception e) { @@ -128,21 +134,31 @@ public class PdeCompiler implements PdeMessageConsumer { "jikes is missing from your PATH,\n" + "see readme.txt for help.", null); return false; + + } else { + e.printStackTrace(); + result = -1; } - e.printStackTrace(); - result = -1; } + // an error was queued up by message(), barf this back to build() + // which will barf it back to PdeEditor. if you're having trouble + // discerning the imagery, consider how cows regurgitate their food + // to digest it, and the fact that they have five stomaches. + // + if (exception != null) throw exception; + // if the result isn't a known, expected value it means that something // is fairly wrong, one possibility is that jikes has crashed. // if (result != 0 && result != 1 ) { - exception = new PdeException(SUPER_BADNESS); - editor.error(exception); - PdeBase.openURL(BUGS_URL); + //exception = new PdeException(SUPER_BADNESS); + //editor.error(exception); // this will instead be thrown + PdeBase.openURL(BUGS_URL); + throw new PdeException(SUPER_BADNESS); } - return (result == 0) ? true : false; + return (result == 0); // ? true : false; } @@ -166,12 +182,26 @@ public class PdeCompiler implements PdeMessageConsumer { // so replace any platform-specific separator characters before // attemping to compare // - String partialTempPath = buildPath.replace(File.separatorChar, '/') - + "/" + className + ".java"; + String buildPathSubst = buildPath.replace(File.separatorChar, '/') + "/"; + + String partialTempPath = null; + int partialStartIndex = -1; //s.indexOf(partialTempPath); + int fileIndex = -1; // use this to build a better exception + + // iterate through the project files to see who's causing the trouble + for (int i = 0; i < sketch.codeCount; i++) { + partialTempPath = buildPathSubst + sketch.code[i].preprocName; + partialStartIndex = s.indexOf(partialTempPath); + if (partialStartIndex != -1) { + fileIndex = i; + break; + } + } + //+ className + ".java"; // if the partial temp path appears in the error message... // - int partialStartIndex = s.indexOf(partialTempPath); + //int partialStartIndex = s.indexOf(partialTempPath); if (partialStartIndex != -1) { // skip past the path and parse the int after the first colon @@ -193,7 +223,7 @@ public class PdeCompiler implements PdeMessageConsumer { return; } - // if we're here at all, this is at least the first error + // if executing at this point, this is *at least* the first error firstErrorFound = true; //err += "error:".length(); @@ -201,21 +231,23 @@ public class PdeCompiler implements PdeMessageConsumer { description = description.trim(); //System.out.println("description = " + description); exception = new PdeException(description, lineNumber-1); - editor.error(exception); + + // NOTE!! major change here, this exception will be queued + // here to be thrown by the compile() function + //editor.error(exception); } else { System.err.println("i suck: " + s); } } else { - // this isn't the start of an error line, so don't attempt to parse // a line number out of it. - // if we're not yet at the second error, these lines are probably - // associated with the first error message, which is already in the - // status bar, and are likely to be of interest to the user, so - // spit them to the console. + // if the second error hasn't been discovered yet, these lines + // are probably associated with the first error message, + // which is already in the status bar, and are likely to be + // of interest to the user, so spit them to the console. // if (!secondErrorFound) { System.err.println(s); @@ -240,16 +272,6 @@ public class PdeCompiler implements PdeMessageConsumer { } - /** - * Return the java.class.path, along with additional items - * that were magically gleaned from the 'include' folder, - * which may contain additional library classes. - */ - static public String calcClassPath(File include) { - return System.getProperty("java.class.path") + contentsToClassPath(include); - } - - /// @@ -295,6 +317,12 @@ public class PdeCompiler implements PdeMessageConsumer { } + /** + * Generate a list of packages for an import list + * based on the contents of a classpath. + * @param path the input classpath + * @return array of possible package names + */ static public String[] magicImports(String path) { String imports[] = new String[100]; int importCount = 0; @@ -348,6 +376,9 @@ public class PdeCompiler implements PdeMessageConsumer { } + /** + * Support function for magicImports() + */ static public int magicImportsRecursive(File dir, String sofar, String imports[], int importCount) { diff --git a/processing/app/PdeSketch.java b/processing/app/PdeSketch.java index 9b530bac3..1812c34e7 100644 --- a/processing/app/PdeSketch.java +++ b/processing/app/PdeSketch.java @@ -24,16 +24,18 @@ public class PdeSketch { static String TEMP_BUILD_PATH = "lib" + File.separator + "build"; + static String tempBuildFolder; // name of sketch, which is the name of main file // (without .pde or .java extension) String name; - // - String path; // path to 'main' file for this sketch + String mainFilename; // name of 'main' file, used by load() + //String path; // path to 'main' file for this sketch - // the sketch folder - File folder; + File sketchFolder; + File dataFolder; + File codeFolder; static final int PDE = 0; static final int JAVA = 1; @@ -60,8 +62,8 @@ public class PdeSketch { File mainFile = new File(path); System.out.println("main file is " + mainFile); - main = mainFile.getName(); - System.out.println("main file is " + main); + mainFilename = mainFile.getName(); + System.out.println("main file is " + mainFilename); /* if (main.endsWith(".pde")) { main = main.substring(0, main.length() - 4); @@ -71,9 +73,27 @@ public class PdeSketch { } */ + // lib/build must exist when the application is started + // it is added to the CLASSPATH by default, but if it doesn't + // exist when the application is started, then java will remove + // the entry from the CLASSPATH, causing PdeRuntime to fail. + // + tempBuildFolder = new File(TEMP_BUILD_PATH); + if (!tempBuildFolder.exists()) { + tempBuildFolder.mkdirs(); + PdeBase.showError("Required folder missing", + "A required folder was missing from \n" + + "from your installation of Processing.\n" + + "It has now been replaced, please restart \n" + + "the application to complete the repair.", null); + } + folder = new File(path.getParent()); System.out.println("sketch dir is " + folder); + codeFolder = new File(folder, "code"); + dataFolder = new File(folder, "data"); + load(); } @@ -151,11 +171,12 @@ public class PdeSketch { // move the main class to the first tab // start at 1, if it's at zero, don't bother for (int i = 1; i < codeCount; i++) { - if (code[i].file.getName().equals("main")) { + if (code[i].file.getName().equals(mainFilename)) { System.out.println("found main code at slot " + i); PdeCode temp = code[0]; code[0] = code[i]; code[i] = temp; + break; } } @@ -163,8 +184,6 @@ public class PdeSketch { // it's a dumb, slow sort, but there shouldn't be more than ~5 files for (int i = 1; i < codeCount; i++) { int who = i; - //String who = code[i].name; - //int whoIndex = i; for (int j = i + 1; j < codeCount; j++) { if (code[j].name.compare(code[who].name) < 0) { who = j; // this guy is earlier in the alphabet @@ -209,7 +228,7 @@ public class PdeSketch { filename.toLowerCase().endsWith(".dll") || filename.toLowerCase().endsWith(".jnilib") || filename.toLowerCase().endsWith(".so")) { - File codeFolder = new File(this.folder, "code"); + //File codeFolder = new File(this.folder, "code"); if (!codeFolder.exists()) codeFolder.mkdirs(); destFile = new File(codeFolder, filename); @@ -218,7 +237,7 @@ public class PdeSketch { destFile = new File(this.folder, filename); } else { - File dataFolder = new File(this.folder, "data"); + //File dataFolder = new File(this.folder, "data"); if (!dataFolder.exists()) dataFolder.mkdirs(); destFile = new File(dataFolder, filename); } @@ -266,8 +285,9 @@ public class PdeSketch { // work because the build dir won't exist at startup, so the classloader // will ignore the fact that that dir is in the CLASSPATH in run.sh // - File dirObject = new File(TEMP_BUILD_PATH); - PdeBase.removeDescendants(dirObject); + //File dirObject = new File(TEMP_BUILD_PATH); + //PdeBase.removeDescendants(dirObject); + PdeBase.removeDescendants(tempBuildFolder); } @@ -315,36 +335,26 @@ public class PdeSketch { load(); } - // temporary build folder is inside 'lib' - // this is added to the classpath by default - tempBuildPath = "lib" + File.separator + "build"; - File buildDir = new File(tempBuildPath); - if (!buildDir.exists()) { - buildDir.mkdirs(); - } - // copy contents of data dir into lib/build // TODO write a file sync procedure here.. if the files // already exist in the target, or haven't been modified // don't' bother. this can waste a lot of time when running. - File dataDir = new File(folder, "data"); - if (dataDir.exists()) { + //File dataDir = new File(folder, "data"); + if (dataFolder.exists()) { // just drop the files in the build folder (pre-68) //PdeBase.copyDir(dataDir, buildDir); // drop the files into a 'data' subfolder of the build dir PdeBase.copyDir(dataDir, new File(buildDir, "data")); } - // start with the main - - // make up a temporary class name to suggest - // only used if the code is not in ADVANCED mode + // make up a temporary class name to suggest. + // name will only be used if the code is not in ADVANCED mode. String suggestedClassName = ("Temporary_" + String.valueOf((int) (Math.random() * 10000)) + "_" + String.valueOf((int) (Math.random() * 10000))); // handle preprocessing the main file's code - String mainClassName = build(tempBuildPath, suggestedClassName); + String mainClassName = build(TEMP_BUILD_PATH, suggestedClassName); // externalPaths is magically set by build() // if the compilation worked, run the applet @@ -363,7 +373,7 @@ public class PdeSketch { // get a useful folder name for the 'code' folder // so that it can be included in the java.library.path - String codeFolderPath = ""; + String libraryPath = ""; if (externalCode != null) { libraryPath = externalCode.getCanonicalPath(); } @@ -479,13 +489,16 @@ public class PdeSketch { * * In an advanced program, the returned classname could be different, * which is why the className is set based on the return value. + * A compilation error will burp up a PdeException. + * * @return null if compilation failed, main class name if not */ protected String build(String buildPath, String suggestedClassName) - throws PdeException, Exception { + throws PdeException { - String additionalImports[] = null; - classPath = buildPath; + String importPackageList[] = null; + classPath = buildPath + File.pathSeparator + + System.getProperty("java.class.path"); // figure out the contents of the code folder to see if there // are files that need to be added to the imports @@ -494,11 +507,11 @@ public class PdeSketch { externalRuntime = true; classPath += File.separator + PdeCompiler.contentsToClassPath(codeFolder); - additionalImports = PdeCompiler.magicImports(classPath); + importPackageList = PdeCompiler.magicImports(classPath); libraryPath = codeFolder.getCanonicalPath(); } else { externalRuntime = (codeCount > 1); // at least for now - additionalImports = null; + importPackageList = null; libraryPath = ""; } @@ -517,88 +530,84 @@ public class PdeSketch { String filename = code[i].name + ".java"; PdeBase.saveFile(code[i].program, new File(buildPath, filename)); code[i].preprocName = filename; - continue; - } - PdePreprocessor preprocessor = new PdePreprocessor(); - try { - // if (i != 0) preproc will fail if a pde file is not - // java mode, since that's required - String className = - preprocessor.write(code[i].program, buildPath, - (i == 0) ? suggestedClassName : null, - additionalImports); - if (className == null) { - System.err.println("class could not be determined for " + - code[i].name + " hopefully the error has " + - "already been reported."); - return null; - } else { - code[i].preprocName = className + ".java"; - } - - if (i == 0) { - // store this for the compiler and the runtime - mainClassName = className; - - // check if the 'main' file is in java mode - if (PdePreprocessor.programType == PdePreprocessor.JAVA) { - externalRuntime = true; // we in advanced mode now, boy + } else if (code[i].flavor == PDE) { + PdePreprocessor preprocessor = new PdePreprocessor(); + try { + // if (i != 0) preproc will fail if a pde file is not + // java mode, since that's required + String className = + preprocessor.write(code[i].program, buildPath, + (i == 0) ? suggestedClassName : null, + additionalImports); + if (className == null) { + // TODO this is temporary for debugging + System.err.println("class could not be determined for " + + code[i].name + " hopefully an error has " + + "already been reported."); + return null; + } else { + code[i].preprocName = className + ".java"; } + + if (i == 0) { + // store this for the compiler and the runtime + mainClassName = className; + + // check if the 'main' file is in java mode + if (PdePreprocessor.programType == PdePreprocessor.JAVA) { + externalRuntime = true; // we in advanced mode now, boy + } + } + + } catch (antlr.RecognitionException re) { + // this even returns a column + throw new PdeException(re.getMessage(), + re.getLine() - 1, re.getColumn()); + + } catch (antlr.TokenStreamRecognitionException tsre) { + // while this seems to store line and column internally, + // there doesn't seem to be a method to grab it.. + // so instead it's done using a regexp + + PatternMatcher matcher = new Perl5Matcher(); + PatternCompiler compiler = new Perl5Compiler(); + // line 3:1: unexpected char: 0xA0 + String mess = "^line (\\d+):(\\d+):\\s"; + Pattern pattern = compiler.compile(mess); + + PatternMatcherInput input = + new PatternMatcherInput(tsre.toString()); + if (matcher.contains(input, pattern)) { + MatchResult result = matcher.getMatch(); + + int line = Integer.parseInt(result.group(1).toString()); + int column = Integer.parseInt(result.group(2).toString()); + throw new PdeException(tsre.getMessage(), line-1, column); + + } else { + throw new PdeException(tsre.toString()); + } + + } catch (PdeException pe) { + // PdeExceptions are caught here and re-thrown, so that they don't + // get lost in the more general "Exception" handler below. + throw pe; + + } catch (Exception ex) { + // TODO better method for handling this? + System.err.println("Uncaught exception type:" + ex.getClass()); + ex.printStackTrace(); + throw new PdeException(ex.toString()); } - - } catch (antlr.RecognitionException re) { - // this even returns a column - throw new PdeException(re.getMessage(), - re.getLine() - 1, re.getColumn()); - - } catch (antlr.TokenStreamRecognitionException tsre) { - // while this seems to store line and column internally, - // there doesn't seem to be a method to grab it.. - // so instead it's done using a regexp - - PatternMatcher matcher = new Perl5Matcher(); - PatternCompiler compiler = new Perl5Compiler(); - // line 3:1: unexpected char: 0xA0 - String mess = "^line (\\d+):(\\d+):\\s"; - Pattern pattern = compiler.compile(mess); - - PatternMatcherInput input = - new PatternMatcherInput(tsre.toString()); - if (matcher.contains(input, pattern)) { - MatchResult result = matcher.getMatch(); - - int line = Integer.parseInt(result.group(1).toString()); - int column = Integer.parseInt(result.group(2).toString()); - throw new PdeException(tsre.getMessage(), line-1, column); - - } else { - throw new PdeException(tsre.toString()); - } - - } catch (PdeException pe) { - throw pe; - - } catch (Exception ex) { - System.err.println("Uncaught exception type:" + ex.getClass()); - ex.printStackTrace(); - throw new PdeException(ex.toString()); } } - // compile the program + // compile the program. errors will happen as a PdeException + // that will bubble up to whomever called build(). // - //PdeCompiler compiler = - //new PdeCompiler(buildPath, mainClassName, externalCode, this); - PdeCompiler compiler = new PdeCompiler(this); - - // run the compiler, and funnel errors to the leechErr - // which is a wrapped around - // (this will catch and parse errors during compilation - // the messageStream will call message() for 'compiler') - messageStream = new PdeMessageStream(compiler); - boolean success = compiler.compile(new PrintStream(messageStream)); - + PdeCompiler compiler = new PdeCompiler(); + boolean success = compiler.compile(this, buildPath); return success ? className : null; } @@ -875,16 +884,17 @@ public class PdeSketch { // and the classes may be buried in subfolders if a package name was used // files to include from data directory - if ((dataDir != null) && (dataDir.exists())) { - String datafiles[] = dataDir.list(); - for (int i = 0; i < datafiles.length; i++) { + //if ((dataDir != null) && (dataDir.exists())) { + if (dataFolder.exists()) { + String dataFiles[] = dataFolder.list(); + for (int i = 0; i < dataFiles.length; i++) { // don't export hidden files // skipping dot prefix removes all: . .. .DS_Store - if (datafiles[i].charAt(0) == '.') continue; + if (dataFiles[i].charAt(0) == '.') continue; - entry = new ZipEntry(datafiles[i]); + entry = new ZipEntry(dataFiles[i]); zos.putNextEntry(entry); - zos.write(PdeBase.grabFile(new File(dataDir, datafiles[i]))); + zos.write(PdeBase.grabFile(new File(dataFolder, dataFiles[i]))); zos.closeEntry(); } } @@ -1054,22 +1064,11 @@ public class PdeSketch { } - /** - * Path to the data folder of this sketch. - */ - /* - public File getDataDirectory() { - File dataDir = new File(directory, "data"); - return dataDir.exists() ? dataDir : null; - } - */ - - /** * Returns path to the main .pde file for this sketch. */ public String getMainFilePath() { - return files[0].getAbsolutePath(); + return code[0].file.getAbsolutePath(); } }