diff options
author | Michael 'Mickey' Lauer <mickey@vanille-media.de> | 2005-06-10 20:59:09 +0000 |
---|---|---|
committer | Michael 'Mickey' Lauer <mickey@vanille-media.de> | 2005-06-10 20:59:09 +0000 |
commit | 8cf677f9b2c7bd0928b99d10b3507e10d58830d6 (patch) | |
tree | 0d7eedf6a2c50ca12c6a4d8d617ad9569f12559c | |
parent | 3e08cf83ccf73d84f0783a448d8907e8d9cba388 (diff) | |
download | bitbake-8cf677f9b2c7bd0928b99d10b3507e10d58830d6.tar.gz |
OO overhaul:
- Introduce classes for commands and the command processor
- Autoregister available commands
- Fix 'shell' command to work with unlimited number of parameters
- Remove 'exec' command
-rw-r--r-- | lib/bb/shell.py | 766 |
1 files changed, 392 insertions, 374 deletions
diff --git a/lib/bb/shell.py b/lib/bb/shell.py index 9698bddfa..fb12d7a5f 100644 --- a/lib/bb/shell.py +++ b/lib/bb/shell.py @@ -28,9 +28,10 @@ TODO: * command to reparse just one (or more) bbfile(s) * automatic check if reparsing is necessary (inotify?) * frontend for bb file manipulation? - * pipe output of commands into a shell command (i.e grep or sort)? + * pipe output of commands into shell commands (i.e grep or sort)? * job control, i.e. bring commands into background with '&', fg, bg, etc.? * start parsing in background right after startup? + * print variable from package data * command aliases / shortcuts? """ @@ -46,7 +47,7 @@ import sys, os, imp, readline, socket, httplib, urllib, commands imp.load_source( "bitbake", os.path.dirname( sys.argv[0] )+"/bitbake" ) from bb import data, parse, build, make, fatal -__version__ = "0.4.2" +__version__ = "0.5.0" __credits__ = """BitBake Shell Version %s (C) 2005 Michael 'Mickey' Lauer <mickey@Vanille.de> Type 'help' for more information, press CTRL-D to exit.""" % __version__ @@ -59,152 +60,188 @@ debug = os.environ.get( "BBSHELL_DEBUG", "" ) != "" history_file = "%s/.bbsh_history" % os.environ.get( "HOME" ) ########################################################################## -# Commands +# Class BitBakeShellCommands ########################################################################## -def bufferCommand( params ): - """Dump output buffer #i""" - index = params[0] - print processCommand.memoryOutput.buffer( int( index ) ) - -def buffersCommand( params ): - """Show the available output buffers""" - commands = processCommand.memoryOutput.bufferedCommands() - if not commands: - print "SHELL: No buffered commands available yet. Start doing something." - else: - print "="*35, "Available Output Buffers", "="*27 - for index, cmd in enumerate( commands ): - print "| %s %s" % ( str( index ).ljust( 3 ), cmd ) - print "="*88 - -def buildCommand( params, cmd = "build" ): - """Build a providee""" - name = params[0] - - oldcmd = make.options.cmd - make.options.cmd = cmd - cooker.build_cache = [] - cooker.build_cache_fail = [] - - if not parsed: - print "SHELL: D'oh! The .bb files haven't been parsed yet. Next time call 'parse' before building stuff. This time I'll do it for 'ya." - parseCommand( None ) - try: - cooker.buildProvider( name ) - except build.EventException, e: - print "ERROR: Couldn't build '%s'" % name - global last_exception - last_exception = e - - make.options.cmd = oldcmd - -def cleanCommand( params ): - """Clean a providee""" - buildCommand( params, "clean" ) - -def editCommand( params ): - """Call $EDITOR on a .bb file""" - name = params[0] - os.system( "%s %s" % ( os.environ.get( "EDITOR" ), completeFilePath( name ) ) ) - -def environmentCommand( params ): - """Dump out the outer BitBake environment (see bbread)""" - data.emit_env(sys.__stdout__, make.cfg, True) - -def execCommand( params ): - """Execute one line of python code""" - exec " ".join( params ) in locals(), globals() - -def exitShell( params ): - """Leave the BitBake Shell""" - if debug: print "(setting leave_mainloop to true)" - global leave_mainloop - leave_mainloop = True - -def fileBuildCommand( params, cmd = "build" ): - """Parse and build a .bb file""" - name = params[0] - bf = completeFilePath( name ) - print "SHELL: Calling '%s' on '%s'" % ( cmd, bf ) - - oldcmd = make.options.cmd - make.options.cmd = cmd - cooker.build_cache = [] - cooker.build_cache_fail = [] - - try: - bbfile_data = parse.handle( bf, make.cfg ) - except parse.ParseError: - print "ERROR: Unable to open or parse '%s'" % bf - else: - item = data.getVar('PN', bbfile_data, 1) - data.setVar( "_task_cache", [], bbfile_data ) # force +class BitBakeShellCommands: + """This class contains the valid commands for the shell""" + + def __init__( self, shell ): + """Register all the commands""" + self._shell = shell + for attr in BitBakeShellCommands.__dict__: + if not attr.startswith( "__" ): + if attr.endswith( "_" ): + command = attr[:-1].lower() + else: + command = attr[:].lower() + method = getattr( BitBakeShellCommands, attr ) + if debug: print "registering command '%s'" % command + # scan number of arguments + usage = getattr( method, "usage", "" ) + if usage != "<...>": + numArgs = len( usage.split() ) + else: + numArgs = -1 + shell.registerCommand( command, method, numArgs, "%s %s" % ( command, usage ), method.__doc__ ) + + def buffer( self, params ): + """Dump specified output buffer""" + index = params[0] + print self._shell.myout.buffer( int( index ) ) + buffer.usage = "<index>" + + def buffers( self, params ): + """Show the available output buffers""" + commands = self._shell.myout.bufferedCommands() + if not commands: + print "SHELL: No buffered commands available yet. Start doing something." + else: + print "="*35, "Available Output Buffers", "="*27 + for index, cmd in enumerate( commands ): + print "| %s %s" % ( str( index ).ljust( 3 ), cmd ) + print "="*88 + + def build( self, params, cmd = "build" ): + """Build a providee""" + name = params[0] + + oldcmd = make.options.cmd + make.options.cmd = cmd + cooker.build_cache = [] + cooker.build_cache_fail = [] + + if not parsed: + print "SHELL: D'oh! The .bb files haven't been parsed yet. Next time call 'parse' before building stuff. This time I'll do it for 'ya." + self.parse( None ) try: - cooker.tryBuildPackage( os.path.abspath( bf ), item, bbfile_data ) + cooker.buildProvider( name ) except build.EventException, e: print "ERROR: Couldn't build '%s'" % name global last_exception last_exception = e - make.options.cmd = oldcmd - -def fileCleanCommand( params ): - """Clean a .bb file""" - fileBuildCommand( params, "clean" ) - -def fileRebuildCommand( params ): - """Rebuild (clean & build) a .bb file""" - fileCleanCommand( params ) - fileBuildCommand( params ) + make.options.cmd = oldcmd + build.usage = "<providee>" + + def clean( self, params ): + """Clean a providee""" + self.build( params, "clean" ) + clean.usage = "<providee>" + + def edit( self, params ): + """Call $EDITOR on a .bb file""" + name = params[0] + os.system( "%s %s" % ( os.environ.get( "EDITOR", "vi" ), completeFilePath( name ) ) ) + edit.usage = "<bbfile>" + + def environment( self, params ): + """Dump out the outer BitBake environment (see bbread)""" + data.emit_env(sys.__stdout__, make.cfg, True) + + def exit_( self, params ): + """Leave the BitBake Shell""" + if debug: print "(setting leave_mainloop to true)" + global leave_mainloop + leave_mainloop = True + + def fileBuild( self, params, cmd = "build" ): + """Parse and build a .bb file""" + name = params[0] + bf = completeFilePath( name ) + print "SHELL: Calling '%s' on '%s'" % ( cmd, bf ) + + oldcmd = make.options.cmd + make.options.cmd = cmd + cooker.build_cache = [] + cooker.build_cache_fail = [] -def lastErrorCommand( params ): - """Show the reason or log that was produced by the last BitBake event exception""" - if last_exception is None: - print "SHELL: No Errors yet (Phew)..." - else: - reason, event = last_exception.args - print "SHELL: Reason for the last error: '%s'" % reason - if ':' in reason: - msg, filename = reason.split( ':' ) - filename = filename.strip() - print "SHELL: Dumping log file for last error:" + try: + bbfile_data = parse.handle( bf, make.cfg ) + except parse.ParseError: + print "ERROR: Unable to open or parse '%s'" % bf + else: + item = data.getVar('PN', bbfile_data, 1) + data.setVar( "_task_cache", [], bbfile_data ) # force try: - print open( filename ).read() - except IOError: - print "ERROR: Couldn't open '%s'" % filename - -def newCommand( params ): - """Create a new .bb file and open the editor""" - dirname, filename = params - packages = '/'.join( data.getVar( "BBFILES", make.cfg, 1 ).split('/')[:-2] ) - fulldirname = "%s/%s" % ( packages, dirname ) - - if not os.path.exists( fulldirname ): - print "SHELL: Creating '%s'" % fulldirname - os.mkdir( fulldirname ) - if os.path.exists( fulldirname ) and os.path.isdir( fulldirname ): - if os.path.exists( "%s/%s" % ( fulldirname, filename ) ): - print "SHELL: ERROR: %s/%s already exists" % ( fulldirname, filename ) - return False - print "SHELL: Creating '%s/%s'" % ( fulldirname, filename ) - newpackage = open( "%s/%s" % ( fulldirname, filename ), "w" ) - print >>newpackage,"""DESCRIPTION = "" + cooker.tryBuildPackage( os.path.abspath( bf ), item, bbfile_data ) + except build.EventException, e: + print "ERROR: Couldn't build '%s'" % name + global last_exception + last_exception = e + + make.options.cmd = oldcmd + fileBuild.usage = "<bbfile>" + + def fileClean( self, params ): + """Clean a .bb file""" + self.fileBuild( params, "clean" ) + fileClean.usage = "<bbfile>" + + def fileRebuild( self, params ): + """Rebuild (clean & build) a .bb file""" + self.fileClean( params ) + self.fileBuild( params ) + fileRebuild.usage = "<bbfile>" + + def help( self, params ): + """Show a comprehensive list of commands and their purpose""" + print "="*30, "Available Commands", "="*30 + allcmds = cmds.keys() + allcmds.sort() + for cmd in allcmds: + function,numparams,usage,helptext = cmds[cmd] + print "| %s | %s" % (usage.ljust(30), helptext) + print "="*78 + + def lastError( self, params ): + """Show the reason or log that was produced by the last BitBake event exception""" + if last_exception is None: + print "SHELL: No Errors yet (Phew)..." + else: + reason, event = last_exception.args + print "SHELL: Reason for the last error: '%s'" % reason + if ':' in reason: + msg, filename = reason.split( ':' ) + filename = filename.strip() + print "SHELL: Dumping log file for last error:" + try: + print open( filename ).read() + except IOError: + print "ERROR: Couldn't open '%s'" % filename + + def new( self, params ): + """Create a new .bb file and open the editor""" + dirname, filename = params + packages = '/'.join( data.getVar( "BBFILES", make.cfg, 1 ).split('/')[:-2] ) + fulldirname = "%s/%s" % ( packages, dirname ) + + if not os.path.exists( fulldirname ): + print "SHELL: Creating '%s'" % fulldirname + os.mkdir( fulldirname ) + if os.path.exists( fulldirname ) and os.path.isdir( fulldirname ): + if os.path.exists( "%s/%s" % ( fulldirname, filename ) ): + print "SHELL: ERROR: %s/%s already exists" % ( fulldirname, filename ) + return False + print "SHELL: Creating '%s/%s'" % ( fulldirname, filename ) + newpackage = open( "%s/%s" % ( fulldirname, filename ), "w" ) + print >>newpackage,"""DESCRIPTION = "" SECTION = "" AUTHOR = "" HOMEPAGE = "" MAINTAINER = "" LICENSE = "GPL" +PR = "r0" SRC_URI = "" -inherit base +#inherit base -#do_compile() { +#do_configure() { # #} -#do_configure() { +#do_compile() { # #} @@ -216,126 +253,136 @@ inherit base # #} """ - newpackage.close() - os.system( "%s %s/%s" % ( os.environ.get( "EDITOR" ), fulldirname, filename ) ) - -def pasteBinCommand( params ): - """Send a command + output buffer to http://pastebin.com""" - index = params[0] - contents = processCommand.memoryOutput.buffer( int( index ) ) - status, error, location = sendToPastebin( contents ) - if status == 302: - print "SHELL: Pasted to %s" % location - else: - print "ERROR: %s %s" % ( response.status, response.reason ) + newpackage.close() + os.system( "%s %s/%s" % ( os.environ.get( "EDITOR" ), fulldirname, filename ) ) + new.usage = "<directory> <filename>" + + def pasteBin( self, params ): + """Send a command + output buffer to http://pastebin.com""" + index = params[0] + contents = self._shell.myout.buffer( int( index ) ) + status, error, location = sendToPastebin( contents ) + if status == 302: + print "SHELL: Pasted to %s" % location + else: + print "ERROR: %s %s" % ( response.status, response.reason ) + pasteBin.usage = "<index>" -def pasteLogCommand( params ): - """Send the last event exception error log (if there is one) to http://pastebin.com""" - if last_exception is None: - print "SHELL: No Errors yet (Phew)..." - else: - reason, event = last_exception.args - print "SHELL: Reason for the last error: '%s'" % reason - if ':' in reason: - msg, filename = reason.split( ':' ) - filename = filename.strip() - print "SHELL: Pasting log file to pastebin..." - - status, error, location = sendToPastebin( open( filename ).read() ) - - if status == 302: - print "SHELL: Pasted to %s" % location - else: - print "ERROR: %s %s" % ( response.status, response.reason ) - -def parseCommand( params ): - """(Re-)parse .bb files and calculate the dependency graph""" - cooker.status = cooker.ParsingStatus() - ignore = data.getVar("ASSUME_PROVIDED", make.cfg, 1) or "" - cooker.status.ignored_dependencies = set( ignore.split() ) - cooker.handleCollections( data.getVar("BBFILE_COLLECTIONS", make.cfg, 1) ) - - make.collect_bbfiles( cooker.myProgressCallback ) - cooker.buildDepgraph() - global parsed - parsed = True - print - -def printCommand( params ): - """Print the contents of an outer BitBake environment variable""" - var = params[0] - value = data.getVar( var, make.cfg, 1 ) - print value - -def pythonCommand( params ): - """Enter the expert mode - an interactive BitBake Python Interpreter""" - sys.ps1 = "EXPERT BB>>> " - sys.ps2 = "EXPERT BB... " - import code - python = code.InteractiveConsole( dict( globals() ) ) - python.interact( "SHELL: Expert Mode - BitBake Python %s\nType 'help' for more information, press CTRL-D to switch back to BBSHELL." % sys.version ) - -def setVarCommand( params ): - """Set an outer BitBake environment variable""" - var, value = params - data.setVar( var, value, make.cfg ) - print "OK" - -def rebuildCommand( params ): - """Clean and rebuild a .bb file or a providee""" - buildCommand( params, "clean" ) - buildCommand( params, "build" ) - -def shellCommand( params ): - """Execute a shell command and dump the output""" - print commands.getoutput( " ".join( params ) ) - -def statusCommand( params ): - print "-" * 78 - print "build cache = '%s'" % cooker.build_cache - print "build cache fail = '%s'" % cooker.build_cache_fail - print "building list = '%s'" % cooker.building_list - print "build path = '%s'" % cooker.build_path - print "consider_msgs_cache = '%s'" % cooker.consider_msgs_cache - print "build stats = '%s'" % cooker.stats - if last_exception is not None: print "last_exception = '%s'" % repr( last_exception.args ) - print "memory output contents = '%s'" % processCommand.memoryOutput._buffer - -def testCommand( params ): - """Just for testing...""" - print "testCommand called with '%s'" % params - -def whichCommand( params ): - """Computes the providers for a given providee""" - item = params[0] - - if not parsed: - print "SHELL: D'oh! The .bb files haven't been parsed yet. Next time call 'parse' before building stuff. This time I'll do it for 'ya." - parseCommand( None ) - - preferred = data.getVar( "PREFERRED_PROVIDER_%s" % item, make.cfg, 1 ) - if not preferred: preferred = item - - try: - lv, lf, pv, pf = cooker.findBestProvider( preferred ) - except KeyError: - lv, lf, pv, pf = (None,)*4 - - try: - providers = cooker.status.providers[item] - except KeyError: - print "SHELL: ERROR: Nothing provides", preferred - else: - for provider in providers: - if provider == pf: provider = " (***) %s" % provider - else: provider = " %s" % provider - print provider + def pasteLog( self, params ): + """Send the last event exception error log (if there is one) to http://pastebin.com""" + if last_exception is None: + print "SHELL: No Errors yet (Phew)..." + else: + reason, event = last_exception.args + print "SHELL: Reason for the last error: '%s'" % reason + if ':' in reason: + msg, filename = reason.split( ':' ) + filename = filename.strip() + print "SHELL: Pasting log file to pastebin..." + + status, error, location = sendToPastebin( open( filename ).read() ) + + if status == 302: + print "SHELL: Pasted to %s" % location + else: + print "ERROR: %s %s" % ( response.status, response.reason ) + + def parse( self, params ): + """(Re-)parse .bb files and calculate the dependency graph""" + cooker.status = cooker.ParsingStatus() + ignore = data.getVar("ASSUME_PROVIDED", make.cfg, 1) or "" + cooker.status.ignored_dependencies = set( ignore.split() ) + cooker.handleCollections( data.getVar("BBFILE_COLLECTIONS", make.cfg, 1) ) + + make.collect_bbfiles( cooker.myProgressCallback ) + cooker.buildDepgraph() + global parsed + parsed = True + print + + def print_( self, params ): + """Print the contents of an outer BitBake environment variable""" + var = params[0] + value = data.getVar( var, make.cfg, 1 ) + print value + print_.usage = "<variable>" + + def python( self, params ): + """Enter the expert mode - an interactive BitBake Python Interpreter""" + sys.ps1 = "EXPERT BB>>> " + sys.ps2 = "EXPERT BB... " + import code + interpreter = code.InteractiveConsole( dict( globals() ) ) + interpreter.interact( "SHELL: Expert Mode - BitBake Python %s\nType 'help' for more information, press CTRL-D to switch back to BBSHELL." % sys.version ) + + def setVar( self, params ): + """Set an outer BitBake environment variable""" + var, value = params + data.setVar( var, value, make.cfg ) + print "OK" + setVar.usage = "<variable> <value>" + + def rebuild( self, params ): + """Clean and rebuild a .bb file or a providee""" + self.build( params, "clean" ) + self.build( params, "build" ) + rebuild.usage = "<providee>" + + def shell( self, params ): + """Execute a shell command and dump the output""" + if params != "": + print commands.getoutput( " ".join( params ) ) + shell.usage = "<...>" + + def status( self, params ): + """<just for testing>""" + print "-" * 78 + print "build cache = '%s'" % cooker.build_cache + print "build cache fail = '%s'" % cooker.build_cache_fail + print "building list = '%s'" % cooker.building_list + print "build path = '%s'" % cooker.build_path + print "consider_msgs_cache = '%s'" % cooker.consider_msgs_cache + print "build stats = '%s'" % cooker.stats + if last_exception is not None: print "last_exception = '%s'" % repr( last_exception.args ) + print "memory output contents = '%s'" % self._shell.myout._buffer + + def test( self, params ): + """<just for testing>""" + print "testCommand called with '%s'" % params + + def which( self, params ): + """Computes the providers for a given providee""" + item = params[0] + + if not parsed: + print "SHELL: D'oh! The .bb files haven't been parsed yet. Next time call 'parse' before building stuff. This time I'll do it for 'ya." + self.parse( None ) + + preferred = data.getVar( "PREFERRED_PROVIDER_%s" % item, make.cfg, 1 ) + if not preferred: preferred = item + + try: + lv, lf, pv, pf = cooker.findBestProvider( preferred ) + except KeyError: + lv, lf, pv, pf = (None,)*4 + + try: + providers = cooker.status.providers[item] + except KeyError: + print "SHELL: ERROR: Nothing provides", preferred + else: + for provider in providers: + if provider == pf: provider = " (***) %s" % provider + else: provider = " %s" % provider + print provider + which.usage = "<providee>" ########################################################################## # Common helper functions ########################################################################## def completeFilePath( bbfile ): + """Get the complete bbfile path""" if not make.pkgdata: return bbfile for key in make.pkgdata.keys(): if key.endswith( bbfile ): @@ -343,6 +390,7 @@ def completeFilePath( bbfile ): return bbfile def sendToPastebin( content ): + """Send content to http://www.pastebin.com""" mydata = {} mydata["parent_pid"] = "" mydata["format"] = "bash" @@ -360,11 +408,42 @@ def sendToPastebin( content ): return response.status, response.reason, response.getheader( "location" ) or "unknown" +def completer( text, state ): + """Return a possible readline completion""" + if debug: print "(completer called with text='%s', state='%d'" % ( text, state ) + + if state == 0: + line = readline.get_line_buffer() + if " " in line: + line = line.split() + # we are in second (or more) argument + if line[0] == "print" or line[0] == "set": + allmatches = make.cfg.keys() + elif line[0].startswith( "file" ): + if make.pkgdata is None: allmatches = [ "(No Matches Available. Parsed yet?)" ] + else: allmatches = [ x.split("/")[-1] for x in make.pkgdata.keys() ] + elif line[0] == "build" or line[0] == "clean" or line[0] == "which": + if make.pkgdata is None: allmatches = [ "(No Matches Available. Parsed yet?)" ] + else: allmatches = cooker.status.providers.iterkeys() + else: allmatches = [ "(No tab completion available for this command)" ] + else: + # we are in first argument + allmatches = cmds.iterkeys() + + completer.matches = [ x for x in allmatches if x[:len(text)] == text ] + #print "completer.matches = '%s'" % completer.matches + if len( completer.matches ) > state: + return completer.matches[state] + else: + return None + + ########################################################################## -# File-like output class buffering the output of the last 10 commands +# Class MemoryOutput ########################################################################## class MemoryOutput: + """File-like output class buffering the output of the last 10 commands""" def __init__( self, delegate ): self.delegate = delegate self._buffer = [] @@ -400,152 +479,91 @@ class MemoryOutput: return self.delegate.isatty() ########################################################################## -# Startup / Shutdown +# Class BitBakeShell ########################################################################## -def init(): - """Register commands and set up readline""" - registerCommand( "help", showHelp ) - registerCommand( "exit", exitShell ) - - registerCommand( "buffer", bufferCommand, 1, "buffer <#>" ) - registerCommand( "buffers", buffersCommand, 0 ) - registerCommand( "build", buildCommand, 1, "build <providee>" ) - registerCommand( "clean", cleanCommand, 1, "clean <providee>" ) - registerCommand( "edit", editCommand, 1, "edit <bbfile>" ) - registerCommand( "environment", environmentCommand ) - registerCommand( "exec", execCommand, 1, "exec <one line of pythoncode>" ) - registerCommand( "filebuild", fileBuildCommand, 1, "filebuild <bbfile>" ) - registerCommand( "fileclean", fileCleanCommand, 1, "fileclean <bbfile>" ) - registerCommand( "filerebuild", fileRebuildCommand, 1, "filerebuild <bbfile>" ) - registerCommand( "lastlog", lastErrorCommand, 0 ) - registerCommand( "new", newCommand, 2, "new <directory> <bbfile>" ) - registerCommand( "pastebin", pasteBinCommand, 1, "pastebin <#>" ) - registerCommand( "pastelog", pasteLogCommand, 0 ) - registerCommand( "parse", parseCommand ) - registerCommand( "print", printCommand, 1, "print <variable>" ) - registerCommand( "python", pythonCommand ) - registerCommand( "rebuild", rebuildCommand, 1, "rebuild <providee>" ) - registerCommand( "set", setVarCommand, 2, "set <variable> <value>" ) - registerCommand( "shell", shellCommand, 1, "shell <command>" ) - registerCommand( "status", statusCommand ) - registerCommand( "test", testCommand ) - registerCommand( "which", whichCommand, 1, "which <providee>" ) - - readline.set_completer( completer ) - readline.set_completer_delims( " " ) - readline.parse_and_bind("tab: complete") - - try: - global history_file - readline.read_history_file( history_file ) - except IOError: - pass # It doesn't exist yet. - -def cleanup(): - """Write readline history and clean up resources""" - if debug: print "(writing command history)" - try: - global history_file - readline.write_history_file( history_file ) - except: - print "SHELL: Unable to save command history" +class BitBakeShell: -def completer( text, state ): - """Return a possible readline completion""" - if debug: print "(completer called with text='%s', state='%d'" % ( text, state ) + def __init__( self ): + """Register commands and set up readline""" + self.commands = BitBakeShellCommands( self ) + self.myout = MemoryOutput( sys.stdout ) - if state == 0: - line = readline.get_line_buffer() - if " " in line: - line = line.split() - # we are in second (or more) argument - if line[0] == "print" or line[0] == "set": - allmatches = make.cfg.keys() - elif line[0].startswith( "file" ): - if make.pkgdata is None: allmatches = [ "(No Matches Available. Parsed yet?)" ] - else: allmatches = [ x.split("/")[-1] for x in make.pkgdata.keys() ] - elif line[0] == "build" or line[0] == "clean" or line[0] == "which": - if make.pkgdata is None: allmatches = [ "(No Matches Available. Parsed yet?)" ] - else: allmatches = cooker.status.providers.iterkeys() - else: allmatches = [ "(No tab completion available for this command)" ] - else: - # we are in first argument - allmatches = cmds.iterkeys() - - completer.matches = [ x for x in allmatches if x[:len(text)] == text ] - #print "completer.matches = '%s'" % completer.matches - if len( completer.matches ) > state: - return completer.matches[state] - else: - return None + readline.set_completer( completer ) + readline.set_completer_delims( " " ) + readline.parse_and_bind("tab: complete") -def showCredits(): - """Show credits (sic!)""" - print __credits__ - -def showHelp( *args ): - """Show a comprehensive list of commands and their purpose""" - print "="*35, "Available Commands", "="*35 - allcmds = cmds.keys() - allcmds.sort() - for cmd in allcmds: - function,numparams,usage,helptext = cmds[cmd] - print "| %s | %s" % (usage.ljust(35), helptext) - print "="*88 - -def registerCommand( command, function, numparams = 0, usage = "", helptext = "" ): - """Register a command""" - if usage == "": usage = command - if helptext == "": helptext = function.__doc__ or "<not yet documented>" - cmds[command] = ( function, numparams, usage, helptext ) - -def processCommand( command, params ): - """Process a command. Check number of params and print a usage string, if appropriate""" - if debug: print "(processing command '%s'...)" % command - try: - function, numparams, usage, helptext = cmds[command] - except KeyError: - print "SHELL: ERROR: '%s' command is not a valid command." % command - processCommand.memoryOutput.removeLast() - else: - if not len( params ) == numparams: - print "Usage: '%s'" % usage - return + try: + global history_file + readline.read_history_file( history_file ) + except IOError: + pass # It doesn't exist yet. - result = function( params ) - if debug: print "(result was '%s')" % result + print __credits__ -def main(): - """The main command loop""" - processCommand.memoryOutput = MemoryOutput( sys.stdout ) - while not leave_mainloop: + def cleanup( self ): + """Write readline history and clean up resources""" + if debug: print "(writing command history)" try: - sys.stdout = processCommand.memoryOutput.delegate - cmdline = raw_input( "BB>> " ) - sys.stdout = processCommand.memoryOutput - if cmdline: - commands = cmdline.split( ';' ) - for command in commands: - processCommand.memoryOutput.startCommand( command ) - if ' ' in command: - processCommand( command.split()[0], command.split()[1:] ) - else: - processCommand( command, "" ) - processCommand.memoryOutput.endCommand() - except EOFError: - print - return - except KeyboardInterrupt: - print + global history_file + readline.write_history_file( history_file ) + except: + print "SHELL: Unable to save command history" + + def registerCommand( self, command, function, numparams = 0, usage = "", helptext = "" ): + """Register a command""" + if usage == "": usage = command + if helptext == "": helptext = function.__doc__ or "<not yet documented>" + cmds[command] = ( function, numparams, usage, helptext ) + + def processCommand( self, command, params ): + """Process a command. Check number of params and print a usage string, if appropriate""" + if debug: print "(processing command '%s'...)" % command + try: + function, numparams, usage, helptext = cmds[command] + except KeyError: + print "SHELL: ERROR: '%s' command is not a valid command." % command + self.myout.removeLast() + else: + if (numparams != -1) and (not len( params ) == numparams): + print "Usage: '%s'" % usage + return + + result = function( self.commands, params ) + if debug: print "(result was '%s')" % result + + def main( self ): + """The main command loop""" + while not leave_mainloop: + try: + sys.stdout = self.myout.delegate + cmdline = raw_input( "BB>> " ) + sys.stdout = self.myout + if cmdline: + commands = cmdline.split( ';' ) + for command in commands: + self.myout.startCommand( command ) + if ' ' in command: + self.processCommand( command.split()[0], command.split()[1:] ) + else: + self.processCommand( command, "" ) + self.myout.endCommand() + except EOFError: + print + return + except KeyboardInterrupt: + print + +########################################################################## +# Start function - called from the BitBake command line utility +########################################################################## def start( aCooker ): global cooker cooker = aCooker - showCredits() - init() - main() - cleanup() + bbshell = BitBakeShell() + bbshell.main() + bbshell.cleanup() if __name__ == "__main__": print "SHELL: Sorry, this program should only be called by BitBake." |