summaryrefslogtreecommitdiffstats
path: root/plugins/org.yocto.bc.ui/src/org/yocto/bc/bitbake/BBSession.java
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/org.yocto.bc.ui/src/org/yocto/bc/bitbake/BBSession.java')
-rw-r--r--plugins/org.yocto.bc.ui/src/org/yocto/bc/bitbake/BBSession.java739
1 files changed, 739 insertions, 0 deletions
diff --git a/plugins/org.yocto.bc.ui/src/org/yocto/bc/bitbake/BBSession.java b/plugins/org.yocto.bc.ui/src/org/yocto/bc/bitbake/BBSession.java
new file mode 100644
index 0000000..e998bcd
--- /dev/null
+++ b/plugins/org.yocto.bc.ui/src/org/yocto/bc/bitbake/BBSession.java
@@ -0,0 +1,739 @@
1/*****************************************************************************
2 * Copyright (c) 2009 Ken Gilmer
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 * Ken Gilmer - initial API and implementation
10 *******************************************************************************/
11package org.yocto.bc.bitbake;
12
13import java.io.BufferedReader;
14import java.io.File;
15import java.io.FileFilter;
16import java.io.IOException;
17import java.io.StringReader;
18import java.util.ArrayList;
19import java.util.Arrays;
20import java.util.Collection;
21import java.util.Hashtable;
22import java.util.Iterator;
23import java.util.List;
24import java.util.Map;
25import java.util.Set;
26import java.util.Stack;
27import java.util.concurrent.locks.ReentrantReadWriteLock;
28import java.util.concurrent.locks.Lock;
29
30import org.eclipse.core.resources.IFile;
31import org.eclipse.core.resources.IProject;
32import org.eclipse.core.resources.IResource;
33import org.eclipse.core.runtime.IProgressMonitor;
34import org.eclipse.core.runtime.IStatus;
35import org.eclipse.core.runtime.Status;
36import org.eclipse.jface.preference.JFacePreferences;
37import org.eclipse.jface.resource.JFaceResources;
38import org.eclipse.ui.console.ConsolePlugin;
39import org.eclipse.ui.console.IConsole;
40import org.eclipse.ui.console.IConsoleManager;
41import org.eclipse.ui.console.MessageConsole;
42import org.eclipse.ui.console.MessageConsoleStream;
43import org.eclipse.ui.progress.WorkbenchJob;
44
45import org.yocto.bc.ui.model.IModelElement;
46import org.yocto.bc.ui.model.ProjectInfo;
47
48/**
49 * BBSession encapsulates a global bitbake configuration and is the primary interface
50 * for actions against a BitBake installation.
51 *
52 * @author kgilmer
53 *
54 */
55public class BBSession implements IBBSessionListener, IModelElement, Map {
56 public static final int TYPE_VARIABLE_ASSIGNMENT = 1;
57 public static final int TYPE_UNKNOWN = 2;
58 public static final int TYPE_STATEMENT = 3;
59 public static final int TYPE_FLAG = 4;
60
61 public static final String BUILDDIR_INDICATORS [] = {
62 File.separatorChar + "conf" + File.separatorChar + "local.conf",
63 File.separatorChar + "conf" + File.separatorChar + "bblayers.conf",
64 };
65
66 protected final ProjectInfo pinfo;
67 protected final ShellSession shell;
68 protected Map properties = null;
69 protected List <String> depends = null;
70 protected boolean initialized = false;
71 protected MessageConsole sessionConsole;
72 private final ReentrantReadWriteLock rwlock = new ReentrantReadWriteLock();
73 private final Lock rlock = rwlock.readLock();
74 private final Lock wlock = rwlock.writeLock();
75 protected String parsingCmd;
76 private boolean silent = false;
77
78 public BBSession(ShellSession ssession, String projectRoot) throws IOException {
79 shell = ssession;
80 this.pinfo = new ProjectInfo();
81 pinfo.setLocation(projectRoot);
82 pinfo.setInitScriptPath(ProjectInfoHelper.getInitScriptPath(projectRoot));
83 this.parsingCmd = "DISABLE_SANITY_CHECKS=1 bitbake -e";
84 }
85
86 public BBSession(ShellSession ssession, String projectRoot, boolean silent) throws IOException {
87 this(ssession, projectRoot);
88 this.silent = silent;
89 }
90
91 private Collection adapttoIPath(List<File> asList, IProject project) {
92
93 List pathList = new ArrayList();
94
95 for (Iterator i = asList.iterator(); i.hasNext();) {
96 File f = (File) i.next();
97 IFile ff = project.getFile(stripLeading(f.toString(), project.getLocationURI().getPath()));
98 if (ff.exists()) {
99 pathList.add(ff);
100 }
101 }
102
103 return pathList;
104 }
105
106 private String appendAll(String[] elems, int st) {
107 StringBuffer sb = new StringBuffer();
108
109 for (int i = st; i < elems.length; ++i) {
110 sb.append(elems[i]);
111 }
112
113 return sb.toString();
114 }
115
116 private int charCount(String trimmed, char c) {
117 int i = 0;
118 int p = 0;
119
120 while ((p = trimmed.indexOf(c, p)) > -1) {
121 i++;
122 p++;
123 }
124
125 return i;
126 }
127
128 public void clear() {
129 throw new RuntimeException("BB configuration is read-only.");
130 }
131
132 public boolean containsKey(Object arg0) {
133 try {
134 checkValidAndLock(true);
135 return properties.containsKey(arg0);
136 } catch (Exception e) {
137 e.printStackTrace();
138 return false;
139 }finally {
140 rlock.unlock();
141 }
142 }
143
144 public boolean containsValue(Object arg0) {
145 try {
146 checkValidAndLock(true);
147 return properties.containsValue(arg0);
148 } catch (Exception e) {
149 e.printStackTrace();
150 return false;
151 }finally {
152 rlock.unlock();
153 }
154 }
155
156 public Set entrySet() {
157 try {
158 checkValidAndLock(true);
159 return properties.entrySet();
160 } catch (Exception e) {
161 e.printStackTrace();
162 return null;
163 }finally {
164 rlock.unlock();
165 }
166 }
167
168 @Override
169 public boolean equals(Object arg0) {
170 try {
171 checkValidAndLock(true);
172 return properties.equals(arg0);
173 } catch (Exception e) {
174 e.printStackTrace();
175 return false;
176 }finally {
177 rlock.unlock();
178 }
179 }
180
181 public ShellSession getShell() {
182 return shell;
183 }
184
185 public String getProjInfoRoot() {
186 return pinfo.getRootPath();
187 }
188
189 /**
190 * Recursively generate list of Recipe files from a root directory.
191 *
192 * @param rootDir
193 * @param recipes
194 * @param fileExtension
195 * @param project
196 */
197 private void findRecipes(File rootDir, List recipes, final String fileExtension, IProject project) {
198 File[] children = rootDir.listFiles(new FileFilter() {
199
200 public boolean accept(File pathname) {
201 return pathname.isFile() && pathname.getName().endsWith(fileExtension);
202 }
203
204 });
205
206 if (children != null && children.length > 0) {
207 recipes.addAll(adapttoIPath(Arrays.asList(children), project));
208 }
209
210 File[] childDirs = rootDir.listFiles(new FileFilter() {
211
212 public boolean accept(File pathname) {
213 return pathname.isDirectory();
214 }
215
216 });
217
218 if (childDirs != null && childDirs.length > 0) {
219 for (int i = 0; i < childDirs.length; ++i) {
220 findRecipes(childDirs[i], recipes, fileExtension, project);
221 }
222 }
223 }
224
225 private Collection findRecipes(List paths, IProject project) {
226 List recipes = new ArrayList();
227
228 for (Iterator i = paths.iterator(); i.hasNext();) {
229 String rawPath = (String) i.next();
230 String[] elems = rawPath.split("\\*/\\*");
231
232 if (elems.length == 2) {
233
234 File rootDir = new File(elems[0]);
235
236 findRecipes(rootDir, recipes, elems[1], project);
237 }
238 }
239
240 return recipes;
241 }
242
243 public Object get(Object arg0) {
244 try {
245 checkValidAndLock(true);
246 return properties.get(arg0);
247 } catch (Exception e) {
248 e.printStackTrace();
249 return null;
250 }finally {
251 rlock.unlock();
252 }
253 }
254
255 private List getBitBakeKeywords() {
256 return Arrays.asList(BBLanguageHelper.BITBAKE_KEYWORDS);
257 }
258
259 /**
260 * @return A MessageConsole for this BB session.
261 */
262 public MessageConsole getConsole() {
263 if (sessionConsole == null) {
264 String cName = ProjectInfoHelper.getProjectName(pinfo.getRootPath()) + " Console";
265 IConsoleManager conMan = ConsolePlugin.getDefault().getConsoleManager();
266 IConsole[] existing = conMan.getConsoles();
267 for (int i = 0; i < existing.length; i++)
268 if (cName.equals(existing[i].getName())) {
269 sessionConsole = (MessageConsole) existing[i];
270 break;
271 }
272 if (sessionConsole == null) {
273 sessionConsole = new MessageConsole(cName, null);
274 conMan.addConsoles(new IConsole[] { sessionConsole });
275 }
276 }
277
278 ConsolePlugin.getDefault().getConsoleManager().showConsoleView(sessionConsole);
279
280 return sessionConsole;
281 }
282
283 private int getLineType(String line) {
284
285 if (line.contains("=")) {
286 return TYPE_VARIABLE_ASSIGNMENT;
287 }
288
289 for (Iterator i = getBitBakeKeywords().iterator(); i.hasNext();) {
290 if (line.startsWith((String) i.next())) {
291 return TYPE_STATEMENT;
292 }
293 }
294
295 if (line.contains(":")) {
296 return TYPE_FLAG;
297 }
298
299 return TYPE_UNKNOWN;
300 }
301
302 public Collection getRecipeFiles(IProject project) {
303 try {
304 checkValidAndLock(true);
305 if (!initialized) {
306 throw new RuntimeException(this.getClass().getName() + " is not initialized.");
307 }
308 String bbfiles = (String) this.properties.get("BBFILES");
309 List paths = parseBBFiles(bbfiles);
310 return findRecipes(paths, project);
311 } catch (Exception e) {
312 return null;
313 }
314 finally {
315 rlock.unlock();
316 }
317 }
318
319 @Override
320 public int hashCode() {
321 try {
322 checkValidAndLock(true);
323 return properties.hashCode();
324 } catch (Exception e) {
325 e.printStackTrace();
326 return 0;
327 }finally {
328 rlock.unlock();
329 }
330 }
331
332 protected int checkExecuteError(String result, int code) {
333 String recipe = getDefaultDepends();
334 String text = "Parsing " + ((recipe != null) ? ("recipe " + recipe) : "base configurations");
335 if (code != 0) {
336 text = text + " ERROR!\n" + result;
337 }else {
338 text = text + " SUCCESS.\n";
339 }
340 if(!silent) {
341 displayInConsole(text, code, false);
342 }
343 return code;
344 }
345
346 protected void displayInConsole(final String result, final int code, boolean clear) {
347 MessageConsole console = getConsole();
348 final MessageConsoleStream info = console.newMessageStream();
349 if(clear)
350 console.clearConsole();
351 new WorkbenchJob("Display parsing result") {
352 public IStatus runInUIThread(IProgressMonitor monitor) {
353 if(code != 0) {
354 info.setColor(JFaceResources.getColorRegistry().get(JFacePreferences.ERROR_COLOR));
355 }
356 try {
357 info.println(result);
358 info.close();
359 }catch (Exception e) {
360 e.printStackTrace();
361 }
362 return Status.OK_STATUS;
363 }
364 }.schedule();
365 }
366
367 private void checkValidAndLock(boolean rdlck) throws Exception {
368 if(rdlck)
369 rlock.lock();
370 else
371 wlock.lock();
372 if(!initialized) {
373 //upgrade lock manually
374 if(rdlck) {
375 rlock.unlock();
376 wlock.lock();
377 }
378 try {
379 if(!initialized) { //recheck
380 int [] codes = {-1};
381 String result = shell.execute(parsingCmd, codes);
382 if(checkExecuteError(result, codes[0]) == 0) {
383 properties = parseBBEnvironment(result);
384 } else {
385 properties = parseBBEnvironment("");
386 }
387 initialized = true;
388 }
389 } finally {
390 //downgrade lock
391 if(rdlck) {
392 rlock.lock();
393 wlock.unlock();
394 }
395 }
396 }
397 //not release lock
398 }
399
400 public void initialize() throws Exception {
401 try {
402 checkValidAndLock(false);
403 }finally {
404 wlock.unlock();
405 }
406 }
407
408 private boolean isBlockEnd(String trimmed) {
409 return charCount(trimmed, '}') > charCount(trimmed, '{');
410 // return trimmed.indexOf('}') > -1 && trimmed.indexOf('{') == -1;
411 }
412
413 private boolean isBlockStart(String trimmed) {
414 return charCount(trimmed, '{') > charCount(trimmed, '}');
415 // return trimmed.indexOf('{') > -1 && trimmed.indexOf('}') == -1;
416 }
417
418 public boolean isEmpty() {
419 try {
420 checkValidAndLock(true);
421 return properties.isEmpty();
422 } catch (Exception e) {
423 e.printStackTrace();
424 return true;
425 }finally {
426 rlock.unlock();
427 }
428 }
429
430 public Set keySet() {
431 try {
432 checkValidAndLock(true);
433 return properties.keySet();
434 } catch (Exception e) {
435 e.printStackTrace();
436 return null;
437 }finally {
438 rlock.unlock();
439 }
440 }
441
442 protected void parse(String content, Map outMap) throws Exception {
443 BufferedReader reader = new BufferedReader(new StringReader(content));
444 String line;
445 boolean inLine = false;
446 StringBuffer sb = null;
447 Stack blockStack = new Stack();
448
449 while ((line = reader.readLine()) != null) {
450 String trimmed = line.trim();
451 if (trimmed.length() == 0 || line.startsWith("#")) {
452 // weed out the blank and comment lines
453 continue;
454 }
455 // Now we look for block start ends, and ignore all code within
456 // blocks.
457 if (isBlockStart(trimmed)) {
458 blockStack.push(trimmed);
459 } else if (isBlockEnd(trimmed)) {
460 blockStack.pop();
461
462 }
463
464 if (!blockStack.isEmpty()) {
465 // we are in a code block, continue until we break into global
466 // scope.
467 continue;
468 }
469 if (trimmed.endsWith("\\")) {
470 if (!inLine) {
471 inLine = true;
472 sb = new StringBuffer(trimmed.substring(0, trimmed.length() - 1));
473 } else {
474 sb.append(trimmed.substring(0, trimmed.length() - 1));
475 }
476 // Only parse the line when we have the complete contents.
477 continue;
478 } else if (inLine) {
479 inLine = false;
480 line = sb.toString();
481 }
482
483 parseLine(line, outMap);
484 }
485 }
486
487 private void parseAdditiveAssignment(String line, String operator, Map mo) throws Exception {
488 String[] elems = splitAssignment(line, "\\+=");
489
490 if (elems.length != 2) {
491 throw new Exception("Unable to parse additive variable assignment in line: " + line);
492 }
493
494 if (!mo.containsKey(elems[0])) {
495 mo.put(elems[0].trim(), elems[1]);
496 } else {
497 String existing = (String) mo.get(elems[0]);
498 if (operator.equals("+=")) {
499 mo.put(elems[0], existing + elems[1]);
500 } else {
501 mo.put(elems[0], elems[1] + existing);
502 }
503 }
504 }
505
506 protected String getDefaultDepends() {
507 return null;
508 }
509
510 protected Map parseBBEnvironment(String bbOut) throws Exception {
511 Map env = new Hashtable();
512 this.depends = new ArrayList<String>();
513
514 parse(bbOut, env);
515
516 String included = (String) env.get("BBINCLUDED");
517 if(getDefaultDepends() != null) {
518 this.depends.add(getDefaultDepends());
519 }
520 if(included != null) {
521 this.depends.addAll(Arrays.asList(included.split(" ")));
522 }
523
524 return env;
525 }
526
527
528 private List parseBBFiles(String bbfiles) {
529 return Arrays.asList(bbfiles.split(" "));
530 }
531
532 //Map delegate methods
533
534 private void parseConditionalAssignment(String line, Map mo) throws Exception {
535 String[] elems = splitAssignment(line, "\\?=");
536
537 if (elems.length != 2) {
538 throw new Exception("Unable to parse conditional variable assignment in line: " + line);
539 }
540
541 if (!mo.containsKey(elems[0].trim())) {
542 mo.put(elems[0].trim(), elems[1].trim());
543 }
544 }
545
546 private void parseImmediateAssignment(String line, String delimiter, Map mo) throws Exception {
547 String[] elems = splitAssignment(line, delimiter);
548
549 mo.put(elems[0], substitute(elems[1], mo));
550 }
551
552 private void parseKeyValue(String line, String delimiter, Map mo) throws Exception {
553 String[] elems = splitAssignment(line, delimiter);
554
555 mo.put(elems[0], elems[1]);
556 }
557
558 private void parseLine(String line, Map mo) throws Exception {
559
560 switch (getLineType(line)) {
561 case TYPE_VARIABLE_ASSIGNMENT:
562 parseVariableAssignment(line, mo);
563 break;
564 case TYPE_STATEMENT:
565 case TYPE_FLAG:
566 // for now ignore statements
567 break;
568 case TYPE_UNKNOWN:
569 // we'll gloss over unknown lines as well;
570 break;
571 default:
572 throw new Exception("Unable to parse line: " + line);
573 }
574 }
575
576 private void parseVariableAssignment(String line, Map mo) throws Exception {
577 if (line.contains("?=")) {
578 parseConditionalAssignment(line, mo);
579 } else if (line.contains("+=")) {
580 parseAdditiveAssignment(line, "+=", mo);
581 } else if (line.contains("=+")) {
582 parseAdditiveAssignment(line, "=+", mo);
583 } else if (line.contains(":=")) {
584 parseImmediateAssignment(line, ":=", mo);
585 } else {
586 parseKeyValue(line, "=", mo);
587 }
588
589 }
590
591 private List parseVars(String line) {
592 List l = new ArrayList();
593
594 int i = 0;
595
596 while ((i = line.indexOf("${", i)) > -1) {
597 int i2 = line.indexOf("}", i);
598
599 l.add(line.subSequence(i + 2, i2));
600 i++;
601 }
602
603 return l;
604 }
605
606 public Object put(Object arg0, Object arg1) {
607 throw new RuntimeException("BB configuration is read-only.");
608 }
609
610 public void putAll(Map arg0) {
611 throw new RuntimeException("BB configuration is read-only.");
612 }
613
614 public Object remove(Object arg0) {
615 throw new RuntimeException("BB configuration is read-only.");
616 }
617
618 private String removeQuotes(String line) {
619 line = line.trim();
620
621 if (line.startsWith("\"")) {
622 line = line.substring(1);
623 }
624
625 if (line.endsWith("\"")) {
626 line = line.substring(0, line.length() - 1);
627 }
628
629 return line;
630 }
631
632 public int size() {
633 try {
634 checkValidAndLock(true);
635 return properties.size();
636 }catch (Exception e) {
637 e.printStackTrace();
638 return 0;
639 }finally {
640 rlock.unlock();
641 }
642 }
643
644 private String[] splitAssignment(String line, String seperator) throws Exception {
645 String[] elems = line.split(seperator);
646
647 if (elems.length < 2) {
648 throw new Exception("Unable to parse assignment in line: " + line);
649 } else if (elems.length == 2) {
650
651 elems[0] = elems[0].trim(); // Clean up trailing or leading spaces.
652 if (elems[0].startsWith("export ")) {
653 elems[0] = elems[0].substring("export ".length()).trim();
654 }
655 elems[1] = removeQuotes(elems[1]); // Evaluate variables
656
657 return elems;
658 } else {
659 String[] retVal = new String[2];
660
661 retVal[0] = elems[0];
662 if (retVal[0].startsWith("export ")) {
663 retVal[0] = retVal[0].substring("export ".length()).trim();
664 }
665 retVal[1] = appendAll(elems, 1);
666
667 return retVal;
668 }
669 }
670
671 private String stripLeading(String target, String leading) {
672 if (target.startsWith(leading)) {
673 target = target.substring(leading.length());
674 }
675
676 return target;
677 }
678
679 /**
680 * Return a string with variable substitutions in place.
681 *
682 * @param expression
683 * @return Input string with any substitutions from this file.
684 */
685 public String substitute(String expression, Map mo) {
686
687 List vars = parseVars(expression);
688
689 for (Iterator i = vars.iterator(); i.hasNext();) {
690 String varName = (String) i.next();
691 String varToken = "${" + varName + "}";
692
693 if (mo.containsKey(varName)) {
694 expression = expression.replace(varToken, (String) mo.get(varName));
695 } else if (System.getProperty(varName) != null) {
696 expression = expression.replace(varToken, System.getProperty(varName));
697 } else if (varName.toUpperCase().equals("HOME")) {
698 expression = expression.replace(varToken, System.getProperty("user.home"));
699 }
700 }
701
702 return expression;
703 }
704
705 public Collection values() {
706 try {
707 checkValidAndLock(true);
708 return properties.values();
709 } catch (Exception e) {
710 e.printStackTrace();
711 return null;
712 }finally {
713 rlock.unlock();
714 }
715 }
716
717 public void changeNotified(IResource[] added, IResource[] removed, IResource[] changed) {
718 wlock.lock();
719 try {
720 if (initialized && (removed != null || changed != null)) {
721 for(int i=0;removed != null && i<removed.length;i++) {
722 if (this.depends.contains(removed[i].getLocation().toString())) {
723 initialized = false;
724 return;
725 }
726 }
727 for(int i=0;changed != null && i<changed.length;i++) {
728 if (this.depends.contains(changed[i].getLocation().toString())) {
729 initialized = false;
730 return;
731 }
732 }
733 }
734 }
735 finally {
736 wlock.unlock();
737 }
738 }
739}