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