mirror of
https://github.com/arduino/Arduino.git
synced 2025-02-27 21:54:30 +01:00
445 lines
14 KiB
Java
Executable File
445 lines
14 KiB
Java
Executable File
/*
|
|
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() + "\"");
|
|
}
|
|
}
|
|
|
|
}
|