diff options
Diffstat (limited to 'org.openembedded.bc.ui/src/org/openembedded/bc/bitbake')
8 files changed, 1104 insertions, 0 deletions
diff --git a/org.openembedded.bc.ui/src/org/openembedded/bc/bitbake/BBCommonVars.java b/org.openembedded.bc.ui/src/org/openembedded/bc/bitbake/BBCommonVars.java new file mode 100644 index 0000000..0bd1a44 --- /dev/null +++ b/org.openembedded.bc.ui/src/org/openembedded/bc/bitbake/BBCommonVars.java @@ -0,0 +1,24 @@ +/***************************************************************************** + * Copyright (c) 2009 Ken Gilmer + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Ken Gilmer - initial API and implementation + *******************************************************************************/ +package org.openembedded.bc.bitbake; + +/** + * Constants for commonly used bitbake variables. + * @author kgilmer + * + */ +public class BBCommonVars { + public final static String WORKDIR = "WORKDIR"; + public static final String PN = "PN"; + public static final String S = "S"; + public static final String PV = "PV"; + +} diff --git a/org.openembedded.bc.ui/src/org/openembedded/bc/bitbake/BBLanguageHelper.java b/org.openembedded.bc.ui/src/org/openembedded/bc/bitbake/BBLanguageHelper.java new file mode 100644 index 0000000..249bdee --- /dev/null +++ b/org.openembedded.bc.ui/src/org/openembedded/bc/bitbake/BBLanguageHelper.java @@ -0,0 +1,61 @@ +/***************************************************************************** + * Copyright (c) 2009 Ken Gilmer + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Ken Gilmer - initial API and implementation + *******************************************************************************/ +package org.openembedded.bc.bitbake; + +import java.util.Comparator; +import java.util.Map; +import java.util.TreeMap; + +/** + * Here is where all BitBake-related information is centralized. + * @author kgilmer + * + */ +public class BBLanguageHelper { + + public static final String[] BITBAKE_KEYWORDS = new String[] { "inherit", "require", "export", "addtask", "python", "include"}; + public static final String[] SHELL_KEYWORDS = new String[] { "while", "do", "if", "fi", "ln", "export", "install", "oe_libinstall", "for", "in", "done", "echo", "then", "cat", "rm", "rmdir", "mkdir", "printf", "exit", "test", "cd", "cp"}; + public static final String[] BITBAKE_STANDARD_FUNCTIONS = new String[] { "stage", "configure", "compile", "install" }; + public static final String BITBAKE_RECIPE_FILE_EXTENSION = "bb"; + + /** + * @return A map of names and descriptions of commonly used BitBake variables. + */ + public static Map getCommonBitbakeVariables() { + Map m = new TreeMap(new Comparator() { + + public int compare(Object o1, Object o2) { + + return ((String) o1).compareTo(((String) o2)); + } + + }); + + m.put("SECTION", "Category of package"); + m.put("PR", "Package Release Number"); + m.put("SRC_URI", "Location of package sources"); + m.put("DESCRIPTION", "Description of package"); + m.put("EXTRA_OEMAKE", "Extra flags to pass to the package makefile"); + m.put("EXTRA_OECONF", "Extra configuration flags for the package makefile"); + m.put("DEPENDS", "The set of build-time dependent packages"); + m.put("RDEPENDS", "The set of run-time dependent packages"); + m.put("HOMEPAGE", "Homepage of the package"); + m.put("LICENSE", "License of the package"); + m.put("FILES_${PN}", "Full file path of files on target."); + m.put("S", "Package source directory"); + m.put("PV", "Package version"); + m.put("AUTHOR", "Author or maintainer of package"); + m.put("PRIORITY", "Priority of package"); + + return m; + } + +} diff --git a/org.openembedded.bc.ui/src/org/openembedded/bc/bitbake/BBRecipe.java b/org.openembedded.bc.ui/src/org/openembedded/bc/bitbake/BBRecipe.java new file mode 100644 index 0000000..3321abd --- /dev/null +++ b/org.openembedded.bc.ui/src/org/openembedded/bc/bitbake/BBRecipe.java @@ -0,0 +1,45 @@ +/***************************************************************************** + * Copyright (c) 2009 Ken Gilmer + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Ken Gilmer - initial API and implementation + *******************************************************************************/ +package org.openembedded.bc.bitbake; + +import java.io.IOException; + +/** + * Represents the bitbake environment of a recipe package. + * @author kgilmer + * + */ +public class BBRecipe extends BBSession { + private final BBSession session; + private final String filePath; + + public BBRecipe(BBSession session, String filePath) throws IOException { + super(session.shell, session.pinfo.getRootPath()); + this.session = session; + this.filePath = filePath; + } + + @Override + public void initialize() throws Exception { + if (initialized) { + return; + } + + String ret = shell.execute("bitbake -e -b " + filePath); + properties = parseBBEnvironment(ret); + + if (ret == null || properties.size() == 0) { + throw new IOException("Failed to parse " + filePath); + } + + initialized = true; + } +} diff --git a/org.openembedded.bc.ui/src/org/openembedded/bc/bitbake/BBSession.java b/org.openembedded.bc.ui/src/org/openembedded/bc/bitbake/BBSession.java new file mode 100644 index 0000000..87b3f53 --- /dev/null +++ b/org.openembedded.bc.ui/src/org/openembedded/bc/bitbake/BBSession.java @@ -0,0 +1,513 @@ +/***************************************************************************** + * Copyright (c) 2009 Ken Gilmer + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Ken Gilmer - initial API and implementation + *******************************************************************************/ +package org.openembedded.bc.bitbake; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileFilter; +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Stack; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.ui.console.ConsolePlugin; +import org.eclipse.ui.console.IConsole; +import org.eclipse.ui.console.MessageConsole; +import org.openembedded.bc.ui.model.IModelElement; +import org.openembedded.bc.ui.model.ProjectInfo; + + +/** + * BBSession encapsulates a global bitbake configuration and is the primary interface + * for actions against a BitBake installation. + * + * @author kgilmer + * + */ +public class BBSession implements IModelElement, Map { + public static final int TYPE_VARIABLE_ASSIGNMENT = 1; + public static final int TYPE_UNKNOWN = 2; + public static final int TYPE_STATEMENT = 3; + public static final int TYPE_FLAG = 4; + + protected final ProjectInfo pinfo; + protected final ShellSession shell; + protected Map properties = null; + protected boolean initialized = false; + private MessageConsole sessionConsole; + + public BBSession(ShellSession ssession, String projectRoot) throws IOException { + shell = ssession; + this.pinfo = new ProjectInfo(); + pinfo.setLocation(projectRoot); + pinfo.setInitScriptPath(ProjectInfoHelper.getInitScriptPath(projectRoot)); + } + + private Collection adapttoIPath(List<File> asList, IProject project) { + + List pathList = new ArrayList(); + + for (Iterator i = asList.iterator(); i.hasNext();) { + File f = (File) i.next(); + IFile ff = project.getFile(stripLeading(f.toString(), project.getLocationURI().getPath())); + if (ff.exists()) { + pathList.add(ff); + } + } + + return pathList; + } + + private String appendAll(String[] elems, int st) { + StringBuffer sb = new StringBuffer(); + + for (int i = st; i < elems.length; ++i) { + sb.append(elems[i]); + } + + return sb.toString(); + } + + private int charCount(String trimmed, char c) { + int i = 0; + int p = 0; + + while ((p = trimmed.indexOf(c, p)) > -1) { + i++; + p++; + } + + return i; + } + + public void clear() { + throw new RuntimeException("BB configuration is read-only."); + } + + public boolean containsKey(Object arg0) { + return properties.containsKey(arg0); + } + + public boolean containsValue(Object arg0) { + return properties.containsValue(arg0); + } + + public Set entrySet() { + return properties.entrySet(); + } + + @Override + public boolean equals(Object arg0) { + return properties.equals(arg0); + } + + public ShellSession getShell() { + return shell; + } + + /** + * Recursively generate list of Recipe files from a root directory. + * + * @param rootDir + * @param recipes + * @param fileExtension + * @param project + */ + private void findRecipes(File rootDir, List recipes, final String fileExtension, IProject project) { + File[] children = rootDir.listFiles(new FileFilter() { + + public boolean accept(File pathname) { + return pathname.isFile() && pathname.getName().endsWith(fileExtension); + } + + }); + + if (children != null && children.length > 0) { + recipes.addAll(adapttoIPath(Arrays.asList(children), project)); + } + + File[] childDirs = rootDir.listFiles(new FileFilter() { + + public boolean accept(File pathname) { + return pathname.isDirectory(); + } + + }); + + if (childDirs != null && childDirs.length > 0) { + for (int i = 0; i < childDirs.length; ++i) { + findRecipes(childDirs[i], recipes, fileExtension, project); + } + } + } + + private Collection findRecipes(List paths, IProject project) { + List recipes = new ArrayList(); + + for (Iterator i = paths.iterator(); i.hasNext();) { + String rawPath = (String) i.next(); + String[] elems = rawPath.split("\\*/\\*"); + + if (elems.length == 2) { + + File rootDir = new File(elems[0]); + + findRecipes(rootDir, recipes, elems[1], project); + } + } + + return recipes; + } + + public Object get(Object arg0) { + return properties.get(arg0); + } + + private List getBitBakeKeywords() { + return Arrays.asList(BBLanguageHelper.BITBAKE_KEYWORDS); + } + + /** + * @return A MessageConsole for this BB session. + */ + public MessageConsole getConsole() { + if (sessionConsole == null) { + String cName = ProjectInfoHelper.getProjectName(pinfo.getRootPath()) + " Console"; + sessionConsole = new MessageConsole(cName, null); + ConsolePlugin.getDefault().getConsoleManager().addConsoles(new IConsole[] { sessionConsole }); + } + + ConsolePlugin.getDefault().getConsoleManager().showConsoleView(sessionConsole); + + return sessionConsole; + } + + private int getLineType(String line) { + + if (line.contains("=")) { + return TYPE_VARIABLE_ASSIGNMENT; + } + + for (Iterator i = getBitBakeKeywords().iterator(); i.hasNext();) { + if (line.startsWith((String) i.next())) { + return TYPE_STATEMENT; + } + } + + if (line.contains(":")) { + return TYPE_FLAG; + } + + return TYPE_UNKNOWN; + } + + public Collection getRecipeFiles(IProject project) { + if (!initialized) { + throw new RuntimeException(this.getClass().getName() + " is not initialized."); + } + String bbfiles = (String) this.properties.get("BBFILES"); + + List paths = parseBBFiles(bbfiles); + + return findRecipes(paths, project); + } + + @Override + public int hashCode() { + return properties.hashCode(); + } + + public void initialize() throws Exception { + if (initialized) { + return; + } + + properties = parseBBEnvironment(shell.execute("bitbake -e")); + initialized = true; + } + + private boolean isBlockEnd(String trimmed) { + return charCount(trimmed, '}') > charCount(trimmed, '{'); + // return trimmed.indexOf('}') > -1 && trimmed.indexOf('{') == -1; + } + + private boolean isBlockStart(String trimmed) { + return charCount(trimmed, '{') > charCount(trimmed, '}'); + // return trimmed.indexOf('{') > -1 && trimmed.indexOf('}') == -1; + } + + public boolean isEmpty() { + return properties.isEmpty(); + } + + public boolean isInitialized() { + return initialized; + } + + public Set keySet() { + return properties.keySet(); + } + + protected void parse(String content, Map outMap) throws Exception { + BufferedReader reader = new BufferedReader(new StringReader(content)); + String line; + boolean inLine = false; + StringBuffer sb = null; + Stack blockStack = new Stack(); + + while ((line = reader.readLine()) != null) { + String trimmed = line.trim(); + if (trimmed.length() == 0 || line.startsWith("#")) { + // weed out the blank and comment lines + continue; + } + // Now we look for block start ends, and ignore all code within + // blocks. + if (isBlockStart(trimmed)) { + blockStack.push(trimmed); + } else if (isBlockEnd(trimmed)) { + blockStack.pop(); + + } + + if (!blockStack.isEmpty()) { + // we are in a code block, continue until we break into global + // scope. + continue; + } + if (trimmed.endsWith("\\")) { + if (!inLine) { + inLine = true; + sb = new StringBuffer(trimmed.substring(0, trimmed.length() - 1)); + } else { + sb.append(trimmed.substring(0, trimmed.length() - 1)); + } + // Only parse the line when we have the complete contents. + continue; + } else if (inLine) { + inLine = false; + line = sb.toString(); + } + + parseLine(line, outMap); + } + } + + private void parseAdditiveAssignment(String line, String operator, Map mo) throws Exception { + String[] elems = splitAssignment(line, "\\+="); + + if (elems.length != 2) { + throw new Exception("Unable to parse additive variable assignment in line: " + line); + } + + if (!mo.containsKey(elems[0])) { + mo.put(elems[0].trim(), elems[1]); + } else { + String existing = (String) mo.get(elems[0]); + if (operator.equals("+=")) { + mo.put(elems[0], existing + elems[1]); + } else { + mo.put(elems[0], elems[1] + existing); + } + } + } + + protected Map parseBBEnvironment(String bbOut) throws Exception { + Map env = new Hashtable(); + + parse(bbOut, env); + + return env; + } + + + private List parseBBFiles(String bbfiles) { + return Arrays.asList(bbfiles.split(" ")); + } + + //Map delegate methods + + private void parseConditionalAssignment(String line, Map mo) throws Exception { + String[] elems = splitAssignment(line, "\\?="); + + if (elems.length != 2) { + throw new Exception("Unable to parse conditional variable assignment in line: " + line); + } + + if (!mo.containsKey(elems[0].trim())) { + mo.put(elems[0].trim(), elems[1].trim()); + } + } + + private void parseImmediateAssignment(String line, String delimiter, Map mo) throws Exception { + String[] elems = splitAssignment(line, delimiter); + + mo.put(elems[0], substitute(elems[1], mo)); + } + + private void parseKeyValue(String line, String delimiter, Map mo) throws Exception { + String[] elems = splitAssignment(line, delimiter); + + mo.put(elems[0], elems[1]); + } + + private void parseLine(String line, Map mo) throws Exception { + + switch (getLineType(line)) { + case TYPE_VARIABLE_ASSIGNMENT: + parseVariableAssignment(line, mo); + break; + case TYPE_STATEMENT: + case TYPE_FLAG: + // for now ignore statements + break; + case TYPE_UNKNOWN: + // we'll gloss over unknown lines as well; + break; + default: + throw new Exception("Unable to parse line: " + line); + } + } + + private void parseVariableAssignment(String line, Map mo) throws Exception { + if (line.contains("?=")) { + parseConditionalAssignment(line, mo); + } else if (line.contains("+=")) { + parseAdditiveAssignment(line, "+=", mo); + } else if (line.contains("=+")) { + parseAdditiveAssignment(line, "=+", mo); + } else if (line.contains(":=")) { + parseImmediateAssignment(line, ":=", mo); + } else { + parseKeyValue(line, "=", mo); + } + + } + + private List parseVars(String line) { + List l = new ArrayList(); + + int i = 0; + + while ((i = line.indexOf("${", i)) > -1) { + int i2 = line.indexOf("}", i); + + l.add(line.subSequence(i + 2, i2)); + i++; + } + + return l; + } + + public Object put(Object arg0, Object arg1) { + throw new RuntimeException("BB configuration is read-only."); + } + + public void putAll(Map arg0) { + throw new RuntimeException("BB configuration is read-only."); + } + + public Object remove(Object arg0) { + throw new RuntimeException("BB configuration is read-only."); + } + + private String removeQuotes(String line) { + line = line.trim(); + + if (line.startsWith("\"")) { + line = line.substring(1); + } + + if (line.endsWith("\"")) { + line = line.substring(0, line.length() - 1); + } + + return line; + } + + public int size() { + return properties.size(); + } + + private String[] splitAssignment(String line, String seperator) throws Exception { + String[] elems = line.split(seperator); + + if (elems.length < 2) { + throw new Exception("Unable to parse assignment in line: " + line); + } else if (elems.length == 2) { + + elems[0] = elems[0].trim(); // Clean up trailing or leading spaces. + if (elems[0].startsWith("export ")) { + elems[0] = elems[0].substring("export ".length()).trim(); + } + elems[1] = removeQuotes(elems[1]); // Evaluate variables + + return elems; + } else { + String[] retVal = new String[2]; + + retVal[0] = elems[0]; + if (retVal[0].startsWith("export ")) { + retVal[0] = retVal[0].substring("export ".length()).trim(); + } + retVal[1] = appendAll(elems, 1); + + return retVal; + } + } + + private String stripLeading(String target, String leading) { + if (target.startsWith(leading)) { + target = target.substring(leading.length()); + } + + return target; + } + + /** + * Return a string with variable substitutions in place. + * + * @param expression + * @return Input string with any substitutions from this file. + */ + public String substitute(String expression, Map mo) { + + List vars = parseVars(expression); + + for (Iterator i = vars.iterator(); i.hasNext();) { + String varName = (String) i.next(); + String varToken = "${" + varName + "}"; + + if (mo.containsKey(varName)) { + expression = expression.replace(varToken, (String) mo.get(varName)); + } else if (System.getProperty(varName) != null) { + expression = expression.replace(varToken, System.getProperty(varName)); + } else if (varName.toUpperCase().equals("HOME")) { + expression = expression.replace(varToken, System.getProperty("user.home")); + } + } + + return expression; + } + + public Collection values() { + return properties.values(); + } +} diff --git a/org.openembedded.bc.ui/src/org/openembedded/bc/bitbake/ICommandResponseHandler.java b/org.openembedded.bc.ui/src/org/openembedded/bc/bitbake/ICommandResponseHandler.java new file mode 100644 index 0000000..bc32895 --- /dev/null +++ b/org.openembedded.bc.ui/src/org/openembedded/bc/bitbake/ICommandResponseHandler.java @@ -0,0 +1,15 @@ +/***************************************************************************** + * Copyright (c) 2009 Ken Gilmer + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Ken Gilmer - initial API and implementation + *******************************************************************************/ +package org.openembedded.bc.bitbake; + +public interface ICommandResponseHandler { + public void response(String line, boolean isError); +} diff --git a/org.openembedded.bc.ui/src/org/openembedded/bc/bitbake/ProjectInfoHelper.java b/org.openembedded.bc.ui/src/org/openembedded/bc/bitbake/ProjectInfoHelper.java new file mode 100644 index 0000000..37cc28b --- /dev/null +++ b/org.openembedded.bc.ui/src/org/openembedded/bc/bitbake/ProjectInfoHelper.java @@ -0,0 +1,102 @@ +/***************************************************************************** + * Copyright (c) 2009 Ken Gilmer + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Ken Gilmer - initial API and implementation + *******************************************************************************/ +package org.openembedded.bc.bitbake; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.ResourcesPlugin; +import org.openembedded.bc.ui.model.ProjectInfo; + + +/** + * A helper class for ProjectInfo related tasks. + * + * @author kgilmer + * + */ +public class ProjectInfoHelper { + + /** + * @param path + * @return The path to bitbake init script + * @throws IOException + */ + public static String getInitScriptPath(String path) throws IOException { + File inFile = new File(path, ".eclipse-data"); + BufferedReader br = new BufferedReader(new FileReader(inFile)); + + String val = br.readLine(); + + br.close(); + + return val; + } + + public static String getInitScript(String path) throws IOException { + File inFile = new File(path); + BufferedReader br = new BufferedReader(new FileReader(inFile)); + StringBuffer sb = new StringBuffer(); + String line = null; + + while ((line = br.readLine()) != null) { + sb.append(line); + } + + br.close(); + + return sb.toString(); + } + + public static String getProjectName(String projectRoot) { + IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects(); + for (int i = 0; i < projects.length; ++i) { + try { + if (projects[i].getLocationURI().getPath().equals(projectRoot)) { + return projects[i].getName(); + } + + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + return null; + } + + /** + * This method will store the path to the bitbake init script for future + * reference. + * + * @param path + * @param projInfo + * @throws IOException + */ + public static void store(String path, ProjectInfo projInfo) throws IOException { + writeToFile(path, projInfo.getInitScriptPath()); + } + + private static void writeToFile(String path, String init) throws IOException { + File outFile = new File(path, ".eclipse-data"); + FileOutputStream fos = new FileOutputStream(outFile); + + fos.write(init.getBytes()); + + fos.flush(); + fos.close(); + } + +} diff --git a/org.openembedded.bc.ui/src/org/openembedded/bc/bitbake/ShellSession.java b/org.openembedded.bc.ui/src/org/openembedded/bc/bitbake/ShellSession.java new file mode 100644 index 0000000..37f478f --- /dev/null +++ b/org.openembedded.bc.ui/src/org/openembedded/bc/bitbake/ShellSession.java @@ -0,0 +1,233 @@ +/***************************************************************************** + * Copyright (c) 2009 Ken Gilmer + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Ken Gilmer - initial API and implementation + *******************************************************************************/ +package org.openembedded.bc.bitbake; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.Writer; + +/** + * A class for Linux shell sessions. + * @author kgilmer + * + */ +public class ShellSession { + /** + * Bash shell + */ + public static final int SHELL_TYPE_BASH = 1; + /** + * sh shell + */ + public static final int SHELL_TYPE_SH = 2; + private volatile boolean interrupt = false; + /** + * String used to isolate command execution + */ + public static final String TERMINATOR = "234o987dsfkcqiuwey18837032843259d"; + public static final String LT = System.getProperty("line.separator"); + + public static String getFilePath(String file) throws IOException { + File f = new File(file); + + if (!f.exists() || f.isDirectory()) { + throw new IOException("Path passed is not a file: " + file); + } + + StringBuffer sb = new StringBuffer(); + + String elems[] = file.split(File.separator); + + for (int i = 0; i < elems.length - 1; ++i) { + sb.append(elems[i]); + sb.append(File.separator); + } + + return sb.toString(); + } + private Process process; + + private OutputStream pos = null; + //private File initFile = null; + private String shellPath = null; + private final String initCmd; + private final File root; + private final Writer out; + + + public ShellSession(int shellType, File root, String initCmd, Writer out) throws IOException { + this.root = root; + this.initCmd = initCmd; + if (out == null) { + this.out = new NullWriter(); + } else { + this.out = out; + } + if (shellType == SHELL_TYPE_SH) { + shellPath = "/bin/sh"; + } + shellPath = "/bin/bash"; + + initializeShell(); + } + + private void initializeShell() throws IOException { + process = Runtime.getRuntime().exec(shellPath); + pos = process.getOutputStream(); + + if (root != null) { + out.write(execute("cd " + root.getAbsolutePath())); + } + + if (initCmd != null) { + out.write(execute("source " + initCmd)); + } + } + + synchronized public String execute(String command) throws IOException { + String errorMessage = null; + interrupt = false; + out.write(command); + out.write(LT); + + sendToProcessAndTerminate(command); + + if (process.getErrorStream().available() > 0) { + byte[] msg = new byte[process.getErrorStream().available()]; + + process.getErrorStream().read(msg, 0, msg.length); + out.write(new String(msg)); + out.write(LT); + errorMessage = "Error while executing: " + command + LT + new String(msg); + } + + BufferedReader br = new BufferedReader(new InputStreamReader(process + .getInputStream())); + + StringBuffer sb = new StringBuffer(); + String line = null; + + while (((line = br.readLine()) != null) && !line.equals(TERMINATOR) && !interrupt) { + sb.append(line); + sb.append(LT); + out.write(line); + out.write(LT); + } + + if (interrupt) { + process.destroy(); + initializeShell(); + interrupt = false; + } + + if (errorMessage != null) { + throw new IOException(errorMessage); + } + + return sb.toString(); + } + + synchronized public void execute(String command, ICommandResponseHandler handler) throws IOException { + execute(command, TERMINATOR, handler); + } + + synchronized public void execute(String command, String terminator, ICommandResponseHandler handler) throws IOException { + interrupt = false; + InputStream errIs = process.getErrorStream(); + if (errIs.available() > 0) { + clearErrorStream(errIs); + } + sendToProcessAndTerminate(command); + + BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream())); + String std = null; + + do { + if (errIs.available() > 0) { + byte[] msg = new byte[errIs.available()]; + + errIs.read(msg, 0, msg.length); + out.write(new String(msg)); + handler.response(new String(msg), true); + } + + std = br.readLine(); + + if (std != null && !std.equals(terminator)) { + out.write(std); + handler.response(std, false); + } + + } while (std != null && !std.equals(terminator) && !interrupt); + + if (interrupt) { + process.destroy(); + initializeShell(); + interrupt = false; + } + } + + private void clearErrorStream(InputStream is) { + + try { + byte b[] = new byte[is.available()]; + is.read(b); + //System.out.println("clearing: " + new String(b)); + } catch (IOException e) { + e.printStackTrace(); + //Ignore any error + } + } + + /** + * Send command string to shell process and add special terminator string so + * reader knows when output is complete. + * + * @param command + * @throws IOException + */ + private void sendToProcessAndTerminate(String command) throws IOException { + pos.write(command.getBytes()); + pos.write(LT.getBytes()); + pos.flush(); + pos.write("echo ".getBytes()); + pos.write(TERMINATOR.getBytes()); + pos.write(LT.getBytes()); + pos.flush(); + } + + /** + * Interrupt any running processes. + */ + public void interrupt() { + interrupt = true; + } + + private class NullWriter extends Writer { + + @Override + public void close() throws IOException { + } + + @Override + public void flush() throws IOException { + } + + @Override + public void write(char[] cbuf, int off, int len) throws IOException { + } + + } +} diff --git a/org.openembedded.bc.ui/src/org/openembedded/bc/bitbake/ShellTest2.java b/org.openembedded.bc.ui/src/org/openembedded/bc/bitbake/ShellTest2.java new file mode 100644 index 0000000..8ef5ef5 --- /dev/null +++ b/org.openembedded.bc.ui/src/org/openembedded/bc/bitbake/ShellTest2.java @@ -0,0 +1,111 @@ +package org.openembedded.bc.bitbake; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +/** + * Tests for ShellSession. + * + * @author kgilmer + * + */ +public class ShellTest2 { + public static void main(String[] args) { + ShellTest2 st = new ShellTest2(); + + //testSimpleCommands(); + + testStreamResponse(); + } + + private static void streamOut(InputStream executeIS) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(executeIS)); + + String line = null; + while ((line = br.readLine()) != null) { + System.out.println(line); + } + } + + private static void testSimpleCommands() { + try { + ShellSession ss = new ShellSession(ShellSession.SHELL_TYPE_BASH, null, null, null); + System.out.println(ss.execute("echo \"bo is $boo\"")); + + System.out.println(ss.execute("export boo=asdf")); + + System.out.println(ss.execute("echo \"bo is $boo\"")); + + System.out.println(ss.execute("cd /home/kgilmer/dev/workspaces/com.buglabs.build.oe")); + System.out.println(ss.execute("source reinstate-build-env")); + System.out.println(ss.execute("echo $BBPATH")); + System.out.println(ss.execute("bitbake -e")); + + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + private static void testStreamResponse() { + try { + final ShellSession ss = new ShellSession(ShellSession.SHELL_TYPE_BASH, null, null, null); + + ss.execute("./loop.sh", ShellSession.TERMINATOR, new ICommandResponseHandler() { + + public void response(String line, boolean isError) { + if (isError) { + System.out.println("ERROR: " + line); + } else { + System.out.println(line); + ss.interrupt(); + } + + } + }); + + ss.execute("ls /home", ShellSession.TERMINATOR, new ICommandResponseHandler() { + + public void response(String line, boolean isError) { + if (isError) { + System.err.println("ERROR: " + line); + } else { + System.out.println(line); + } + } + }); + + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + +/* *//** + * A reader that will terminate upon reading a specific line. Prevents reader from blocking. + * + * @author kgilmer + *//* + private class LineTerminatingReader extends BufferedReader { + + private final String terminator; + + public LineTerminatingReader(Reader in, String terminator) { + super(in); + this.terminator = terminator; + } + + public String readLine() throws IOException { + String line = null; + + while (((line = this.readLine()) != null) && !line.equals(terminator)) { + return line; + } + + return null; + } + + }*/ +} |