/* Library.java - Library System for Wiring Copyright (c) 2006 Nicholas Zambetti. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ package processing.app; import java.io.*; import java.util.*; import java.util.zip.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; /* * Provides information about and builds a library */ public class Library implements MessageConsumer{ private File libFolder; private LibraryManager libManager; RunnerException exception; static final String BUGS_URL = "https://developer.berlios.de/bugs/?group_id=3590"; static final String SUPER_BADNESS = "Compiler error, please submit this code to " + BUGS_URL; /* * Create a Library */ public Library(LibraryManager manager, File folder) { libFolder = folder; libManager = manager; /* for debug output System.out.println("library: " + getName()); System.out.println("folder: " + getFolder()); System.out.println("built: " + isBuilt()); System.out.println("buildable: " + isBuildable()); System.out.println("o files: " + getObjectFiles().length); System.out.println("c files: " + getCSourceFiles().length); System.out.println("cpp files: " + getCPPSourceFiles().length); */ } /* * Directory of library * @return File object of library's folder */ public File getFolder() { return libFolder; } /* * The name of library * @return String with library name, derived from folder * note: this will be eventually taken from xml description file */ public String getName() { return libFolder.getName(); } /* * Tests if library is built * @return True if library has .o files, false otherwise */ public boolean isBuilt() { FileFilter onlyObjectFiles = new FileFilter() { public boolean accept(File file) { return file.getName().endsWith(".o"); } }; if(0 < (libFolder.listFiles(onlyObjectFiles)).length){ return true; } return false; } /* * Tests if library is buildable * @return True if library has .cpp files, false otherwise */ public boolean isBuildable() { FileFilter onlySourceFiles = new FileFilter() { public boolean accept(File file) { return (file.getName()).endsWith(".cpp"); } }; if(0 < (libFolder.listFiles(onlySourceFiles)).length){ return true; } return false; } /* * Tests if library is unbuilt but buildable * @return True if library has .cpp files but no .o files, false otherwise */ public boolean isUnbuiltBuildable() { if(isBuildable()){ if(!isBuilt()){ return true; } } return false; } /* * Finds examples folder * @return "examples" folder as file object or null */ private File getExamplesFolder() { FileFilter filter = new FileFilter() { public boolean accept(File file) { if(file.isDirectory()){ if((file.getName()).equalsIgnoreCase("examples")){ return true; } } return false; } }; File[] files = libFolder.listFiles(filter); if(files.length > 0){ return files[0]; } return null; } /* * Populates example menu or submenu with files */ private void populateWithExamples(File folder, JMenu menu, ActionListener listener) { FileFilter onlyfolders = new FileFilter() { public boolean accept(File file) { return file.isDirectory(); } }; File[] folders = folder.listFiles(onlyfolders); File file; JMenu submenu; JMenuItem item; for(int i = 0; i < folders.length; ++i){ file = new File(folders[i], folders[i].getName() + ".pde"); if(file.exists()){ item = new JMenuItem(folders[i].getName()); item.setActionCommand(file.getAbsolutePath()); item.addActionListener(listener); menu.add(item); }else{ submenu = new JMenu(folders[i].getName()); populateWithExamples(folders[i], submenu, listener); menu.add(submenu); } } } /* * Builds and returns an examples menu * @return JMenu object with example files, or null if none */ public JMenu getExamplesMenu(ActionListener listener) { JMenu submenu; File examplesFolder = getExamplesFolder(); if(null != examplesFolder){ submenu = new JMenu("Library-" + getName()); populateWithExamples(examplesFolder, submenu, listener); return submenu; } return null; } /* * List of object files for linking * @return Array of library's object files as File objects */ public File[] getObjectFiles() { FileFilter onlyObjectFiles = new FileFilter() { public boolean accept(File file) { return (file.getName()).endsWith(".o"); } }; return libFolder.listFiles(onlyObjectFiles); } /* * List of header source files for inclusion * @return Array of library's header source files as File objects */ public File[] getHeaderFiles() { FileFilter onlyHFiles = new FileFilter() { public boolean accept(File file) { return (file.getName()).endsWith(".h"); } }; return libFolder.listFiles(onlyHFiles); } /* * List of C source files for compiling * @return Array of library's C source files as File objects */ private File[] getCSourceFiles() { FileFilter onlyCFiles = new FileFilter() { public boolean accept(File file) { return (file.getName()).endsWith(".c"); } }; return libFolder.listFiles(onlyCFiles); } /* * List of C++ source files for compiling * @return Array of library's C++ source files as File objects */ private File[] getCPPSourceFiles() { FileFilter onlyCPPFiles = new FileFilter() { public boolean accept(File file) { return (file.getName()).endsWith(".cpp"); } }; return libFolder.listFiles(onlyCPPFiles); } /* * Attempt to build library * @return true on successful build, false otherwise */ public boolean build() throws RunnerException { if(isBuildable()){ String userDir = System.getProperty("user.dir") + File.separator; String[] baseCompileCommandC = new String[] { ((!Base.isMacOS()) ? "tools/avr/bin/avr-gcc" : userDir + "tools/avr/bin/avr-gcc"), "-c", "-g", "-Os", "-Wall", "-mmcu=" + Preferences.get("build.mcu"), "-DF_CPU=" + Preferences.get("build.f_cpu"), "-I" + libManager.getTarget().getPath(), "-I" + getFolder(), }; String[] baseCompileCommandCPP = new String[] { ((!Base.isMacOS()) ? "tools/avr/bin/avr-g++" : userDir + "tools/avr/bin/avr-g++"), "-c", "-g", "-Os", "-Wall", "-fno-exceptions", "-mmcu=" + Preferences.get("build.mcu"), "-DF_CPU=" + Preferences.get("build.f_cpu"), "-I" + libManager.getTarget().getPath(), "-I" + getFolder(), }; // use built lib directories in include paths when searching for headers // this allows libs to use other libs easily String[] libDirs = libManager.getFolderPaths(); String[] compileCommandC = new String[baseCompileCommandC.length + libDirs.length + 2]; String[] compileCommandCPP = new String[baseCompileCommandCPP.length + libDirs.length + 2]; System.arraycopy(baseCompileCommandC, 0, compileCommandC, 0, baseCompileCommandC.length); System.arraycopy(baseCompileCommandCPP, 0, compileCommandCPP, 0, baseCompileCommandCPP.length); for (int i = 0; i < libDirs.length; ++i) { compileCommandC[baseCompileCommandC.length + i] = "-I" + libDirs[i]; compileCommandCPP[baseCompileCommandCPP.length + i] = "-I" + libDirs[i]; } File[] sourcesC = getCSourceFiles(); File[] sourcesCPP = getCPPSourceFiles(); // execute the compiler, and create threads to deal // with the input and error streams // int result = 0; try { String nameSansExtension; Process process; boolean compiling = true; // compile c sources for(int i = 0; i < sourcesC.length; ++i) { nameSansExtension = sourcesC[i].getName(); nameSansExtension = nameSansExtension.substring(0, nameSansExtension.length() - 2); // -2 because ".c" compileCommandC[compileCommandC.length - 2] = sourcesC[i].getPath(); compileCommandC[compileCommandC.length - 1] = "-o" + getFolder() + File.separator + nameSansExtension + ".o"; process = Runtime.getRuntime().exec(compileCommandC); new MessageSiphon(process.getInputStream(), this); new MessageSiphon(process.getErrorStream(), this); // wait for the process to finish. if interrupted // before waitFor returns, continue waiting // compiling = true; while (compiling) { try { result = process.waitFor(); //System.out.println("result is " + result); compiling = false; } catch (InterruptedException ignored) { } } if (exception != null) { exception.hideStackTrace = true; throw exception; } if(result != 0){ return false; } } // compile c++ sources for(int i = 0; i < sourcesCPP.length; ++i) { nameSansExtension = sourcesCPP[i].getName(); nameSansExtension = nameSansExtension.substring(0, nameSansExtension.length() - 4); // -4 because ".cpp" compileCommandCPP[compileCommandCPP.length - 2] = sourcesCPP[i].getPath(); compileCommandCPP[compileCommandCPP.length - 1] = "-o" + getFolder() + File.separator + nameSansExtension + ".o"; process = Runtime.getRuntime().exec(compileCommandCPP); new MessageSiphon(process.getInputStream(), this); new MessageSiphon(process.getErrorStream(), this); // wait for the process to finish. if interrupted // before waitFor returns, continue waiting // compiling = true; while (compiling) { try { result = process.waitFor(); //System.out.println("result is " + result); compiling = false; } catch (InterruptedException ignored) { } } if (exception != null) { exception.hideStackTrace = true; throw exception; } if(result != 0){ return false; } } } catch (Exception e) { String msg = e.getMessage(); if ((msg != null) && (msg.indexOf("avr-gcc: not found") != -1)) { Base.showWarning("Compiler error", "Could not find the compiler.\n" + "avr-gcc is missing from your PATH,\n" + "see readme.txt for help.", null); return false; } else if ((msg != null) && (msg.indexOf("avr-g++: not found") != -1)) { Base.showWarning("Compiler error", "Could not find the compiler.\n" + "avr-g++ is missing from your PATH,\n" + "see readme.txt for help.", null); return false; } else { e.printStackTrace(); result = -1; } } // an error was queued up by message() if (exception != null) { throw exception; } if (result != 0 && result != 1 ) { Base.openURL(BUGS_URL); throw new RunnerException(SUPER_BADNESS); } // success would mean that 'result' is set to zero return (result == 0); // ? true : false; } return false; // library is not buildable (contains no sources) } /** * Part of the MessageConsumer interface, this is called * whenever a piece (usually a line) of error message is spewed * out from the compiler. The errors are parsed for their contents * and line number, which is then reported back to Editor. */ public void message(String inString) { // This receives messages as full lines, so a newline needs // to be added as they're printed to the console. // always print all compilation output for library writers! String outString = ""; // shorten file paths so that they are friendlier int start = 0; int end = 0; String substring = libFolder.getPath() + File.separator; StringBuffer result = new StringBuffer(); while ((end = inString.indexOf(substring, start)) >= 0) { result.append(inString.substring(start, end)); start = end + substring.length(); } result.append(inString.substring(start)); outString = result.toString(); System.err.print(outString); // prepare error for throwing if (inString.indexOf("error") != -1){ exception = new RunnerException("Error building library \"" + getName() + "\""); } } }