2 # ex:ts=4:sw=4:sts=4:et
3 # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
4 ##########################################################################
6 # Copyright (C) 2005-2006 Michael 'Mickey' Lauer <mickey@Vanille.de>
7 # Copyright (C) 2005-2006 Vanille Media
9 # This program is free software; you can redistribute it and/or modify it under
10 # the terms of the GNU General Public License as published by the Free Software
11 # Foundation; version 2 of the License.
13 # This program is distributed in the hope that it will be useful, but WITHOUT
14 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License along with
18 # this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 # Place, Suite 330, Boston, MA 02111-1307 USA.
21 ##########################################################################
24 # * Holger Freyther <zecke@handhelds.org>
25 # * Justin Patrin <papercrane@reversefold.com>
27 ##########################################################################
33 * list defined tasks per package
36 * command to reparse just one (or more) bbfile(s)
37 * automatic check if reparsing is necessary (inotify?)
38 * frontend for bb file manipulation
39 * more shell-like features:
40 - output control, i.e. pipe output into grep, sort, etc.
41 - job control, i.e. bring running commands into background and foreground
42 * start parsing in background right after startup
46 * force doesn't always work
47 * readline completion for commands with more than one parameters
51 ##########################################################################
52 # Import and setup global variables
53 ##########################################################################
58 from sets import Set as set
59 import sys, os, readline, socket, httplib, urllib, commands, popen2, copy, shlex, Queue, fnmatch
60 from bb import data, parse, build, fatal, cache, taskdata, runqueue, providers as Providers
62 __version__ = "0.5.3.1"
63 __credits__ = """BitBake Shell Version %s (C) 2005 Michael 'Mickey' Lauer <mickey@Vanille.de>
64 Type 'help' for more information, press CTRL-D to exit.""" % __version__
67 leave_mainloop = False
72 debug = os.environ.get( "BBSHELL_DEBUG", "" )
74 ##########################################################################
75 # Class BitBakeShellCommands
76 ##########################################################################
78 class BitBakeShellCommands:
79 """This class contains the valid commands for the shell"""
81 def __init__( self, shell ):
82 """Register all the commands"""
84 for attr in BitBakeShellCommands.__dict__:
85 if not attr.startswith( "_" ):
86 if attr.endswith( "_" ):
87 command = attr[:-1].lower()
89 command = attr[:].lower()
90 method = getattr( BitBakeShellCommands, attr )
91 debugOut( "registering command '%s'" % command )
92 # scan number of arguments
93 usage = getattr( method, "usage", "" )
95 numArgs = len( usage.split() )
98 shell.registerCommand( command, method, numArgs, "%s %s" % ( command, usage ), method.__doc__ )
100 def _checkParsed( self ):
102 print "SHELL: This command needs to parse bbfiles..."
105 def _findProvider( self, item ):
107 preferred = data.getVar( "PREFERRED_PROVIDER_%s" % item, cooker.configuration.data, 1 )
108 if not preferred: preferred = item
110 lv, lf, pv, pf = Providers.findBestProvider(preferred, cooker.configuration.data, cooker.status, cooker.build_cache_fail)
112 if item in cooker.status.providers:
113 pf = cooker.status.providers[item][0]
118 def alias( self, params ):
119 """Register a new name for a command"""
122 print "ERROR: Command '%s' not known" % old
124 cmds[new] = cmds[old]
126 alias.usage = "<alias> <command>"
128 def buffer( self, params ):
129 """Dump specified output buffer"""
131 print self._shell.myout.buffer( int( index ) )
132 buffer.usage = "<index>"
134 def buffers( self, params ):
135 """Show the available output buffers"""
136 commands = self._shell.myout.bufferedCommands()
138 print "SHELL: No buffered commands available yet. Start doing something."
140 print "="*35, "Available Output Buffers", "="*27
141 for index, cmd in enumerate( commands ):
142 print "| %s %s" % ( str( index ).ljust( 3 ), cmd )
145 def build( self, params, cmd = "build" ):
146 """Build a providee"""
149 names = globfilter( cooker.status.pkg_pn.keys(), globexpr )
150 if len( names ) == 0: names = [ globexpr ]
151 print "SHELL: Building %s" % ' '.join( names )
153 oldcmd = cooker.configuration.cmd
154 cooker.configuration.cmd = cmd
155 cooker.build_cache = []
156 cooker.build_cache_fail = []
158 td = taskdata.TaskData(cooker.configuration.abort)
163 td.add_provider(cooker.configuration.data, cooker.status, name)
164 providers = td.get_provider(name)
166 if len(providers) == 0:
167 raise Providers.NoProvider
169 tasks.append([name, "do_%s" % cooker.configuration.cmd])
171 td.add_unresolved(cooker.configuration.data, cooker.status)
173 rq = runqueue.RunQueue()
174 rq.prepare_runqueue(cooker.configuration.data, cooker.status, td, tasks)
175 rq.execute_runqueue(cooker, cooker.configuration.data, cooker.status, td, tasks)
177 except Providers.NoProvider:
178 print "ERROR: No Provider"
179 global last_exception
180 last_exception = Providers.NoProvider
182 except runqueue.TaskFailure, fnids:
184 print "ERROR: '%s' failed" % td.fn_index[fnid])
185 global last_exception
186 last_exception = runqueue.TaskFailure
188 except build.EventException, e:
189 print "ERROR: Couldn't build '%s'" % names
190 global last_exception
193 cooker.configuration.cmd = oldcmd
195 build.usage = "<providee>"
197 def clean( self, params ):
198 """Clean a providee"""
199 self.build( params, "clean" )
200 clean.usage = "<providee>"
202 def compile( self, params ):
203 """Execute 'compile' on a providee"""
204 self.build( params, "compile" )
205 compile.usage = "<providee>"
207 def configure( self, params ):
208 """Execute 'configure' on a providee"""
209 self.build( params, "configure" )
210 configure.usage = "<providee>"
212 def edit( self, params ):
213 """Call $EDITOR on a providee"""
215 bbfile = self._findProvider( name )
216 if bbfile is not None:
217 os.system( "%s %s" % ( os.environ.get( "EDITOR", "vi" ), bbfile ) )
219 print "ERROR: Nothing provides '%s'" % name
220 edit.usage = "<providee>"
222 def environment( self, params ):
223 """Dump out the outer BitBake environment (see bbread)"""
224 data.emit_env(sys.__stdout__, cooker.configuration.data, True)
226 def exit_( self, params ):
227 """Leave the BitBake Shell"""
228 debugOut( "setting leave_mainloop to true" )
229 global leave_mainloop
230 leave_mainloop = True
232 def fetch( self, params ):
233 """Fetch a providee"""
234 self.build( params, "fetch" )
235 fetch.usage = "<providee>"
237 def fileBuild( self, params, cmd = "build" ):
238 """Parse and build a .bb file"""
240 bf = completeFilePath( name )
241 print "SHELL: Calling '%s' on '%s'" % ( cmd, bf )
243 oldcmd = cooker.configuration.cmd
244 cooker.configuration.cmd = cmd
245 cooker.build_cache = []
246 cooker.build_cache_fail = []
248 thisdata = copy.deepcopy( initdata )
249 # Caution: parse.handle modifies thisdata, hence it would
250 # lead to pollution cooker.configuration.data, which is
251 # why we use it on a safe copy we obtained from cooker right after
252 # parsing the initial *.conf files
254 bbfile_data = parse.handle( bf, thisdata )
255 except parse.ParseError:
256 print "ERROR: Unable to open or parse '%s'" % bf
258 item = data.getVar('PN', bbfile_data, 1)
259 data.setVar( "_task_cache", [], bbfile_data ) # force
261 cooker.tryBuildPackage( os.path.abspath( bf ), item, cmd, bbfile_data, True )
262 except build.EventException, e:
263 print "ERROR: Couldn't build '%s'" % name
264 global last_exception
267 cooker.configuration.cmd = oldcmd
268 fileBuild.usage = "<bbfile>"
270 def fileClean( self, params ):
271 """Clean a .bb file"""
272 self.fileBuild( params, "clean" )
273 fileClean.usage = "<bbfile>"
275 def fileEdit( self, params ):
276 """Call $EDITOR on a .bb file"""
278 os.system( "%s %s" % ( os.environ.get( "EDITOR", "vi" ), completeFilePath( name ) ) )
279 fileEdit.usage = "<bbfile>"
281 def fileRebuild( self, params ):
282 """Rebuild (clean & build) a .bb file"""
283 self.fileBuild( params, "rebuild" )
284 fileRebuild.usage = "<bbfile>"
286 def fileReparse( self, params ):
287 """(re)Parse a bb file"""
289 print "SHELL: Parsing '%s'" % bbfile
290 parse.update_mtime( bbfile )
291 cooker.bb_cache.cacheValidUpdate(bbfile)
292 fromCache = cooker.bb_cache.loadData(bbfile, cooker.configuration.data)
293 cooker.bb_cache.sync()
294 if False: #fromCache:
295 print "SHELL: File has not been updated, not reparsing"
297 print "SHELL: Parsed"
298 fileReparse.usage = "<bbfile>"
300 def abort( self, params ):
301 """Toggle abort task execution flag (see bitbake -k)"""
302 cooker.configuration.abort = not cooker.configuration.abort
303 print "SHELL: Abort Flag is now '%s'" % repr( cooker.configuration.abort )
305 def force( self, params ):
306 """Toggle force task execution flag (see bitbake -f)"""
307 cooker.configuration.force = not cooker.configuration.force
308 print "SHELL: Force Flag is now '%s'" % repr( cooker.configuration.force )
310 def help( self, params ):
311 """Show a comprehensive list of commands and their purpose"""
312 print "="*30, "Available Commands", "="*30
313 allcmds = cmds.keys()
316 function,numparams,usage,helptext = cmds[cmd]
317 print "| %s | %s" % (usage.ljust(30), helptext)
320 def lastError( self, params ):
321 """Show the reason or log that was produced by the last BitBake event exception"""
322 if last_exception is None:
323 print "SHELL: No Errors yet (Phew)..."
325 reason, event = last_exception.args
326 print "SHELL: Reason for the last error: '%s'" % reason
328 msg, filename = reason.split( ':' )
329 filename = filename.strip()
330 print "SHELL: Dumping log file for last error:"
332 print open( filename ).read()
334 print "ERROR: Couldn't open '%s'" % filename
336 def match( self, params ):
337 """Dump all files or providers matching a glob expression"""
338 what, globexpr = params
341 for key in globfilter( cooker.status.pkg_fn.keys(), globexpr ): print key
342 elif what == "providers":
344 for key in globfilter( cooker.status.pkg_pn.keys(), globexpr ): print key
346 print "Usage: match %s" % self.print_.usage
347 match.usage = "<files|providers> <glob>"
349 def new( self, params ):
350 """Create a new .bb file and open the editor"""
351 dirname, filename = params
352 packages = '/'.join( data.getVar( "BBFILES", cooker.configuration.data, 1 ).split('/')[:-2] )
353 fulldirname = "%s/%s" % ( packages, dirname )
355 if not os.path.exists( fulldirname ):
356 print "SHELL: Creating '%s'" % fulldirname
357 os.mkdir( fulldirname )
358 if os.path.exists( fulldirname ) and os.path.isdir( fulldirname ):
359 if os.path.exists( "%s/%s" % ( fulldirname, filename ) ):
360 print "SHELL: ERROR: %s/%s already exists" % ( fulldirname, filename )
362 print "SHELL: Creating '%s/%s'" % ( fulldirname, filename )
363 newpackage = open( "%s/%s" % ( fulldirname, filename ), "w" )
364 print >>newpackage,"""DESCRIPTION = ""
393 os.system( "%s %s/%s" % ( os.environ.get( "EDITOR" ), fulldirname, filename ) )
394 new.usage = "<directory> <filename>"
396 def pasteBin( self, params ):
397 """Send a command + output buffer to the pastebin at http://rafb.net/paste"""
399 contents = self._shell.myout.buffer( int( index ) )
400 sendToPastebin( "output of " + params[0], contents )
401 pasteBin.usage = "<index>"
403 def pasteLog( self, params ):
404 """Send the last event exception error log (if there is one) to http://rafb.net/paste"""
405 if last_exception is None:
406 print "SHELL: No Errors yet (Phew)..."
408 reason, event = last_exception.args
409 print "SHELL: Reason for the last error: '%s'" % reason
411 msg, filename = reason.split( ':' )
412 filename = filename.strip()
413 print "SHELL: Pasting log file to pastebin..."
415 file = open( filename ).read()
416 sendToPastebin( "contents of " + filename, file )
418 def patch( self, params ):
419 """Execute 'patch' command on a providee"""
420 self.build( params, "patch" )
421 patch.usage = "<providee>"
423 def parse( self, params ):
424 """(Re-)parse .bb files and calculate the dependency graph"""
425 cooker.status = cache.CacheData()
426 ignore = data.getVar("ASSUME_PROVIDED", cooker.configuration.data, 1) or ""
427 cooker.status.ignored_dependencies = set( ignore.split() )
428 cooker.handleCollections( data.getVar("BBFILE_COLLECTIONS", cooker.configuration.data, 1) )
430 (filelist, masked) = cooker.collect_bbfiles()
431 cooker.parse_bbfiles(filelist, masked, cooker.myProgressCallback)
432 cooker.buildDepgraph()
437 def reparse( self, params ):
438 """(re)Parse a providee's bb file"""
439 bbfile = self._findProvider( params[0] )
440 if bbfile is not None:
441 print "SHELL: Found bbfile '%s' for '%s'" % ( bbfile, params[0] )
442 self.fileReparse( [ bbfile ] )
444 print "ERROR: Nothing provides '%s'" % params[0]
445 reparse.usage = "<providee>"
447 def getvar( self, params ):
448 """Dump the contents of an outer BitBake environment variable"""
450 value = data.getVar( var, cooker.configuration.data, 1 )
452 getvar.usage = "<variable>"
454 def peek( self, params ):
455 """Dump contents of variable defined in providee's metadata"""
457 bbfile = self._findProvider( name )
458 if bbfile is not None:
459 the_data = cooker.bb_cache.loadDataFull(bbfile, cooker.configuration.data)
460 value = the_data.getVar( var, 1 )
463 print "ERROR: Nothing provides '%s'" % name
464 peek.usage = "<providee> <variable>"
466 def poke( self, params ):
467 """Set contents of variable defined in providee's metadata"""
468 name, var, value = params
469 bbfile = self._findProvider( name )
470 if bbfile is not None:
471 print "ERROR: Sorry, this functionality is currently broken"
472 #d = cooker.pkgdata[bbfile]
473 #data.setVar( var, value, d )
475 # mark the change semi persistant
476 #cooker.pkgdata.setDirty(bbfile, d)
479 print "ERROR: Nothing provides '%s'" % name
480 poke.usage = "<providee> <variable> <value>"
482 def print_( self, params ):
483 """Dump all files or providers"""
487 for key in cooker.status.pkg_fn.keys(): print key
488 elif what == "providers":
490 for key in cooker.status.providers.keys(): print key
492 print "Usage: print %s" % self.print_.usage
493 print_.usage = "<files|providers>"
495 def python( self, params ):
496 """Enter the expert mode - an interactive BitBake Python Interpreter"""
497 sys.ps1 = "EXPERT BB>>> "
498 sys.ps2 = "EXPERT BB... "
500 interpreter = code.InteractiveConsole( dict( globals() ) )
501 interpreter.interact( "SHELL: Expert Mode - BitBake Python %s\nType 'help' for more information, press CTRL-D to switch back to BBSHELL." % sys.version )
503 def showdata( self, params ):
504 """Execute 'showdata' on a providee"""
505 self.build( params, "showdata" )
506 showdata.usage = "<providee>"
508 def setVar( self, params ):
509 """Set an outer BitBake environment variable"""
511 data.setVar( var, value, cooker.configuration.data )
513 setVar.usage = "<variable> <value>"
515 def rebuild( self, params ):
516 """Clean and rebuild a .bb file or a providee"""
517 self.build( params, "clean" )
518 self.build( params, "build" )
519 rebuild.usage = "<providee>"
521 def shell( self, params ):
522 """Execute a shell command and dump the output"""
524 print commands.getoutput( " ".join( params ) )
525 shell.usage = "<...>"
527 def stage( self, params ):
528 """Execute 'stage' on a providee"""
529 self.build( params, "stage" )
530 stage.usage = "<providee>"
532 def status( self, params ):
533 """<just for testing>"""
535 print "build cache = '%s'" % cooker.build_cache
536 print "build cache fail = '%s'" % cooker.build_cache_fail
537 print "building list = '%s'" % cooker.building_list
538 print "build path = '%s'" % cooker.build_path
539 print "consider_msgs_cache = '%s'" % cooker.consider_msgs_cache
540 print "build stats = '%s'" % cooker.stats
541 if last_exception is not None: print "last_exception = '%s'" % repr( last_exception.args )
542 print "memory output contents = '%s'" % self._shell.myout._buffer
544 def test( self, params ):
545 """<just for testing>"""
546 print "testCommand called with '%s'" % params
548 def unpack( self, params ):
549 """Execute 'unpack' on a providee"""
550 self.build( params, "unpack" )
551 unpack.usage = "<providee>"
553 def which( self, params ):
554 """Computes the providers for a given providee"""
559 preferred = data.getVar( "PREFERRED_PROVIDER_%s" % item, cooker.configuration.data, 1 )
560 if not preferred: preferred = item
563 lv, lf, pv, pf = Providers.findBestProvider(preferred, cooker.configuration.data, cooker.status,
564 cooker.build_cache_fail)
566 lv, lf, pv, pf = (None,)*4
569 providers = cooker.status.providers[item]
571 print "SHELL: ERROR: Nothing provides", preferred
573 for provider in providers:
574 if provider == pf: provider = " (***) %s" % provider
575 else: provider = " %s" % provider
577 which.usage = "<providee>"
579 ##########################################################################
580 # Common helper functions
581 ##########################################################################
583 def completeFilePath( bbfile ):
584 """Get the complete bbfile path"""
585 if not cooker.status.pkg_fn: return bbfile
586 for key in cooker.status.pkg_fn.keys():
587 if key.endswith( bbfile ):
591 def sendToPastebin( desc, content ):
592 """Send content to http://oe.pastebin.com"""
594 mydata["lang"] = "Plain Text"
595 mydata["desc"] = desc
596 mydata["cvt_tabs"] = "No"
597 mydata["nick"] = "%s@%s" % ( os.environ.get( "USER", "unknown" ), socket.gethostname() or "unknown" )
598 mydata["text"] = content
599 params = urllib.urlencode( mydata )
600 headers = {"Content-type": "application/x-www-form-urlencoded","Accept": "text/plain"}
603 conn = httplib.HTTPConnection( "%s:80" % host )
604 conn.request("POST", "/paste/paste.php", params, headers )
606 response = conn.getresponse()
609 if response.status == 302:
610 location = response.getheader( "location" ) or "unknown"
611 print "SHELL: Pasted to http://%s%s" % ( host, location )
613 print "ERROR: %s %s" % ( response.status, response.reason )
615 def completer( text, state ):
616 """Return a possible readline completion"""
617 debugOut( "completer called with text='%s', state='%d'" % ( text, state ) )
620 line = readline.get_line_buffer()
623 # we are in second (or more) argument
624 if line[0] in cmds and hasattr( cmds[line[0]][0], "usage" ): # known command and usage
625 u = getattr( cmds[line[0]][0], "usage" ).split()[0]
626 if u == "<variable>":
627 allmatches = cooker.configuration.data.keys()
628 elif u == "<bbfile>":
629 if cooker.status.pkg_fn is None: allmatches = [ "(No Matches Available. Parsed yet?)" ]
630 else: allmatches = [ x.split("/")[-1] for x in cooker.status.pkg_fn.keys() ]
631 elif u == "<providee>":
632 if cooker.status.pkg_fn is None: allmatches = [ "(No Matches Available. Parsed yet?)" ]
633 else: allmatches = cooker.status.providers.iterkeys()
634 else: allmatches = [ "(No tab completion available for this command)" ]
635 else: allmatches = [ "(No tab completion available for this command)" ]
637 # we are in first argument
638 allmatches = cmds.iterkeys()
640 completer.matches = [ x for x in allmatches if x[:len(text)] == text ]
641 #print "completer.matches = '%s'" % completer.matches
642 if len( completer.matches ) > state:
643 return completer.matches[state]
647 def debugOut( text ):
649 sys.stderr.write( "( %s )\n" % text )
651 def columnize( alist, width = 80 ):
653 A word-wrap function that preserves existing line breaks
654 and most spaces in the text. Expects that existing line
655 breaks are posix newlines (\n).
657 return reduce(lambda line, word, width=width: '%s%s%s' %
659 ' \n'[(len(line[line.rfind('\n')+1:])
660 + len(word.split('\n',1)[0]
666 def globfilter( names, pattern ):
667 return fnmatch.filter( names, pattern )
669 ##########################################################################
671 ##########################################################################
674 """File-like output class buffering the output of the last 10 commands"""
675 def __init__( self, delegate ):
676 self.delegate = delegate
681 def startCommand( self, command ):
682 self._command = command
684 def endCommand( self ):
685 if self._command is not None:
686 if len( self._buffer ) == 10: del self._buffer[0]
687 self._buffer.append( ( self._command, self.text ) )
688 def removeLast( self ):
690 del self._buffer[ len( self._buffer ) - 1 ]
693 def lastBuffer( self ):
695 return self._buffer[ len( self._buffer ) -1 ][1]
696 def bufferedCommands( self ):
697 return [ cmd for cmd, output in self._buffer ]
698 def buffer( self, i ):
699 if i < len( self._buffer ):
700 return "BB>> %s\n%s" % ( self._buffer[i][0], "".join( self._buffer[i][1] ) )
701 else: return "ERROR: Invalid buffer number. Buffer needs to be in (0, %d)" % ( len( self._buffer ) - 1 )
702 def write( self, text ):
703 if self._command is not None and text != "BB>> ": self.text.append( text )
704 if self.delegate is not None: self.delegate.write( text )
706 return self.delegate.flush()
708 return self.delegate.fileno()
710 return self.delegate.isatty()
712 ##########################################################################
714 ##########################################################################
718 def __init__( self ):
719 """Register commands and set up readline"""
720 self.commandQ = Queue.Queue()
721 self.commands = BitBakeShellCommands( self )
722 self.myout = MemoryOutput( sys.stdout )
723 self.historyfilename = os.path.expanduser( "~/.bbsh_history" )
724 self.startupfilename = os.path.expanduser( "~/.bbsh_startup" )
726 readline.set_completer( completer )
727 readline.set_completer_delims( " " )
728 readline.parse_and_bind("tab: complete")
731 readline.read_history_file( self.historyfilename )
733 pass # It doesn't exist yet.
737 # save initial cooker configuration (will be reused in file*** commands)
739 initdata = copy.deepcopy( cooker.configuration.data )
742 """Write readline history and clean up resources"""
743 debugOut( "writing command history" )
745 readline.write_history_file( self.historyfilename )
747 print "SHELL: Unable to save command history"
749 def registerCommand( self, command, function, numparams = 0, usage = "", helptext = "" ):
750 """Register a command"""
751 if usage == "": usage = command
752 if helptext == "": helptext = function.__doc__ or "<not yet documented>"
753 cmds[command] = ( function, numparams, usage, helptext )
755 def processCommand( self, command, params ):
756 """Process a command. Check number of params and print a usage string, if appropriate"""
757 debugOut( "processing command '%s'..." % command )
759 function, numparams, usage, helptext = cmds[command]
761 print "SHELL: ERROR: '%s' command is not a valid command." % command
762 self.myout.removeLast()
764 if (numparams != -1) and (not len( params ) == numparams):
765 print "Usage: '%s'" % usage
768 result = function( self.commands, params )
769 debugOut( "result was '%s'" % result )
771 def processStartupFile( self ):
772 """Read and execute all commands found in $HOME/.bbsh_startup"""
773 if os.path.exists( self.startupfilename ):
774 startupfile = open( self.startupfilename, "r" )
775 for cmdline in startupfile:
776 debugOut( "processing startup line '%s'" % cmdline )
780 print "ERROR: '|' in startup file is not allowed. Ignoring line"
782 self.commandQ.put( cmdline.strip() )
785 """The main command loop"""
786 while not leave_mainloop:
788 if self.commandQ.empty():
789 sys.stdout = self.myout.delegate
790 cmdline = raw_input( "BB>> " )
791 sys.stdout = self.myout
793 cmdline = self.commandQ.get()
795 allCommands = cmdline.split( ';' )
796 for command in allCommands:
799 # special case for expert mode
800 if command == 'python':
801 sys.stdout = self.myout.delegate
802 self.processCommand( command, "" )
803 sys.stdout = self.myout
805 self.myout.startCommand( command )
806 if '|' in command: # disable output
807 command, pipecmd = command.split( '|' )
808 delegate = self.myout.delegate
809 self.myout.delegate = None
810 tokens = shlex.split( command, True )
811 self.processCommand( tokens[0], tokens[1:] or "" )
812 self.myout.endCommand()
813 if pipecmd is not None: # restore output
814 self.myout.delegate = delegate
816 pipe = popen2.Popen4( pipecmd )
817 pipe.tochild.write( "\n".join( self.myout.lastBuffer() ) )
819 sys.stdout.write( pipe.fromchild.read() )
824 except KeyboardInterrupt:
827 ##########################################################################
828 # Start function - called from the BitBake command line utility
829 ##########################################################################
831 def start( aCooker ):
834 bbshell = BitBakeShell()
835 bbshell.processStartupFile()
839 if __name__ == "__main__":
840 print "SHELL: Sorry, this program should only be called by BitBake."