summaryrefslogtreecommitdiff
path: root/poky/bitbake/lib/bb/command.py
diff options
context:
space:
mode:
authorDave Cobbley <david.j.cobbley@linux.intel.com>2018-08-14 20:05:37 +0300
committerBrad Bishop <bradleyb@fuzziesquirrel.com>2018-08-23 04:26:31 +0300
commiteb8dc40360f0cfef56fb6947cc817a547d6d9bc6 (patch)
treede291a73dc37168da6370e2cf16c347d1eba9df8 /poky/bitbake/lib/bb/command.py
parent9c3cf826d853102535ead04cebc2d6023eff3032 (diff)
downloadopenbmc-eb8dc40360f0cfef56fb6947cc817a547d6d9bc6.tar.xz
[Subtree] Removing import-layers directory
As part of the move to subtrees, need to bring all the import layers content to the top level. Change-Id: I4a163d10898cbc6e11c27f776f60e1a470049d8f Signed-off-by: Dave Cobbley <david.j.cobbley@linux.intel.com> Signed-off-by: Brad Bishop <bradleyb@fuzziesquirrel.com>
Diffstat (limited to 'poky/bitbake/lib/bb/command.py')
-rw-r--r--poky/bitbake/lib/bb/command.py765
1 files changed, 765 insertions, 0 deletions
diff --git a/poky/bitbake/lib/bb/command.py b/poky/bitbake/lib/bb/command.py
new file mode 100644
index 0000000000..6c966e3dbc
--- /dev/null
+++ b/poky/bitbake/lib/bb/command.py
@@ -0,0 +1,765 @@
+"""
+BitBake 'Command' module
+
+Provide an interface to interact with the bitbake server through 'commands'
+"""
+
+# Copyright (C) 2006-2007 Richard Purdie
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+"""
+The bitbake server takes 'commands' from its UI/commandline.
+Commands are either synchronous or asynchronous.
+Async commands return data to the client in the form of events.
+Sync commands must only return data through the function return value
+and must not trigger events, directly or indirectly.
+Commands are queued in a CommandQueue
+"""
+
+from collections import OrderedDict, defaultdict
+
+import bb.event
+import bb.cooker
+import bb.remotedata
+
+class DataStoreConnectionHandle(object):
+ def __init__(self, dsindex=0):
+ self.dsindex = dsindex
+
+class CommandCompleted(bb.event.Event):
+ pass
+
+class CommandExit(bb.event.Event):
+ def __init__(self, exitcode):
+ bb.event.Event.__init__(self)
+ self.exitcode = int(exitcode)
+
+class CommandFailed(CommandExit):
+ def __init__(self, message):
+ self.error = message
+ CommandExit.__init__(self, 1)
+ def __str__(self):
+ return "Command execution failed: %s" % self.error
+
+class CommandError(Exception):
+ pass
+
+class Command:
+ """
+ A queue of asynchronous commands for bitbake
+ """
+ def __init__(self, cooker):
+ self.cooker = cooker
+ self.cmds_sync = CommandsSync()
+ self.cmds_async = CommandsAsync()
+ self.remotedatastores = bb.remotedata.RemoteDatastores(cooker)
+
+ # FIXME Add lock for this
+ self.currentAsyncCommand = None
+
+ def runCommand(self, commandline, ro_only = False):
+ command = commandline.pop(0)
+ if hasattr(CommandsSync, command):
+ # Can run synchronous commands straight away
+ command_method = getattr(self.cmds_sync, command)
+ if ro_only:
+ if not hasattr(command_method, 'readonly') or False == getattr(command_method, 'readonly'):
+ return None, "Not able to execute not readonly commands in readonly mode"
+ try:
+ self.cooker.process_inotify_updates()
+ if getattr(command_method, 'needconfig', True):
+ self.cooker.updateCacheSync()
+ result = command_method(self, commandline)
+ except CommandError as exc:
+ return None, exc.args[0]
+ except (Exception, SystemExit):
+ import traceback
+ return None, traceback.format_exc()
+ else:
+ return result, None
+ if self.currentAsyncCommand is not None:
+ return None, "Busy (%s in progress)" % self.currentAsyncCommand[0]
+ if command not in CommandsAsync.__dict__:
+ return None, "No such command"
+ self.currentAsyncCommand = (command, commandline)
+ self.cooker.configuration.server_register_idlecallback(self.cooker.runCommands, self.cooker)
+ return True, None
+
+ def runAsyncCommand(self):
+ try:
+ self.cooker.process_inotify_updates()
+ if self.cooker.state in (bb.cooker.state.error, bb.cooker.state.shutdown, bb.cooker.state.forceshutdown):
+ # updateCache will trigger a shutdown of the parser
+ # and then raise BBHandledException triggering an exit
+ self.cooker.updateCache()
+ return False
+ if self.currentAsyncCommand is not None:
+ (command, options) = self.currentAsyncCommand
+ commandmethod = getattr(CommandsAsync, command)
+ needcache = getattr( commandmethod, "needcache" )
+ if needcache and self.cooker.state != bb.cooker.state.running:
+ self.cooker.updateCache()
+ return True
+ else:
+ commandmethod(self.cmds_async, self, options)
+ return False
+ else:
+ return False
+ except KeyboardInterrupt as exc:
+ self.finishAsyncCommand("Interrupted")
+ return False
+ except SystemExit as exc:
+ arg = exc.args[0]
+ if isinstance(arg, str):
+ self.finishAsyncCommand(arg)
+ else:
+ self.finishAsyncCommand("Exited with %s" % arg)
+ return False
+ except Exception as exc:
+ import traceback
+ if isinstance(exc, bb.BBHandledException):
+ self.finishAsyncCommand("")
+ else:
+ self.finishAsyncCommand(traceback.format_exc())
+ return False
+
+ def finishAsyncCommand(self, msg=None, code=None):
+ if msg or msg == "":
+ bb.event.fire(CommandFailed(msg), self.cooker.data)
+ elif code:
+ bb.event.fire(CommandExit(code), self.cooker.data)
+ else:
+ bb.event.fire(CommandCompleted(), self.cooker.data)
+ self.currentAsyncCommand = None
+ self.cooker.finishcommand()
+
+ def reset(self):
+ self.remotedatastores = bb.remotedata.RemoteDatastores(self.cooker)
+
+def split_mc_pn(pn):
+ if pn.startswith("multiconfig:"):
+ _, mc, pn = pn.split(":", 2)
+ return (mc, pn)
+ return ('', pn)
+
+class CommandsSync:
+ """
+ A class of synchronous commands
+ These should run quickly so as not to hurt interactive performance.
+ These must not influence any running synchronous command.
+ """
+
+ def stateShutdown(self, command, params):
+ """
+ Trigger cooker 'shutdown' mode
+ """
+ command.cooker.shutdown(False)
+
+ def stateForceShutdown(self, command, params):
+ """
+ Stop the cooker
+ """
+ command.cooker.shutdown(True)
+
+ def getAllKeysWithFlags(self, command, params):
+ """
+ Returns a dump of the global state. Call with
+ variable flags to be retrieved as params.
+ """
+ flaglist = params[0]
+ return command.cooker.getAllKeysWithFlags(flaglist)
+ getAllKeysWithFlags.readonly = True
+
+ def getVariable(self, command, params):
+ """
+ Read the value of a variable from data
+ """
+ varname = params[0]
+ expand = True
+ if len(params) > 1:
+ expand = (params[1] == "True")
+
+ return command.cooker.data.getVar(varname, expand)
+ getVariable.readonly = True
+
+ def setVariable(self, command, params):
+ """
+ Set the value of variable in data
+ """
+ varname = params[0]
+ value = str(params[1])
+ command.cooker.extraconfigdata[varname] = value
+ command.cooker.data.setVar(varname, value)
+
+ def getSetVariable(self, command, params):
+ """
+ Read the value of a variable from data and set it into the datastore
+ which effectively expands and locks the value.
+ """
+ varname = params[0]
+ result = self.getVariable(command, params)
+ command.cooker.data.setVar(varname, result)
+ return result
+
+ def setConfig(self, command, params):
+ """
+ Set the value of variable in configuration
+ """
+ varname = params[0]
+ value = str(params[1])
+ setattr(command.cooker.configuration, varname, value)
+
+ def enableDataTracking(self, command, params):
+ """
+ Enable history tracking for variables
+ """
+ command.cooker.enableDataTracking()
+
+ def disableDataTracking(self, command, params):
+ """
+ Disable history tracking for variables
+ """
+ command.cooker.disableDataTracking()
+
+ def setPrePostConfFiles(self, command, params):
+ prefiles = params[0].split()
+ postfiles = params[1].split()
+ command.cooker.configuration.prefile = prefiles
+ command.cooker.configuration.postfile = postfiles
+ setPrePostConfFiles.needconfig = False
+
+ def matchFile(self, command, params):
+ fMatch = params[0]
+ return command.cooker.matchFile(fMatch)
+ matchFile.needconfig = False
+
+ def getUIHandlerNum(self, command, params):
+ return bb.event.get_uihandler()
+ getUIHandlerNum.needconfig = False
+ getUIHandlerNum.readonly = True
+
+ def setEventMask(self, command, params):
+ handlerNum = params[0]
+ llevel = params[1]
+ debug_domains = params[2]
+ mask = params[3]
+ return bb.event.set_UIHmask(handlerNum, llevel, debug_domains, mask)
+ setEventMask.needconfig = False
+ setEventMask.readonly = True
+
+ def setFeatures(self, command, params):
+ """
+ Set the cooker features to include the passed list of features
+ """
+ features = params[0]
+ command.cooker.setFeatures(features)
+ setFeatures.needconfig = False
+ # although we change the internal state of the cooker, this is transparent since
+ # we always take and leave the cooker in state.initial
+ setFeatures.readonly = True
+
+ def updateConfig(self, command, params):
+ options = params[0]
+ environment = params[1]
+ cmdline = params[2]
+ command.cooker.updateConfigOpts(options, environment, cmdline)
+ updateConfig.needconfig = False
+
+ def parseConfiguration(self, command, params):
+ """Instruct bitbake to parse its configuration
+ NOTE: it is only necessary to call this if you aren't calling any normal action
+ (otherwise parsing is taken care of automatically)
+ """
+ command.cooker.parseConfiguration()
+ parseConfiguration.needconfig = False
+
+ def getLayerPriorities(self, command, params):
+ command.cooker.parseConfiguration()
+ ret = []
+ # regex objects cannot be marshalled by xmlrpc
+ for collection, pattern, regex, pri in command.cooker.bbfile_config_priorities:
+ ret.append((collection, pattern, regex.pattern, pri))
+ return ret
+ getLayerPriorities.readonly = True
+
+ def getRecipes(self, command, params):
+ try:
+ mc = params[0]
+ except IndexError:
+ mc = ''
+ return list(command.cooker.recipecaches[mc].pkg_pn.items())
+ getRecipes.readonly = True
+
+ def getRecipeDepends(self, command, params):
+ try:
+ mc = params[0]
+ except IndexError:
+ mc = ''
+ return list(command.cooker.recipecaches[mc].deps.items())
+ getRecipeDepends.readonly = True
+
+ def getRecipeVersions(self, command, params):
+ try:
+ mc = params[0]
+ except IndexError:
+ mc = ''
+ return command.cooker.recipecaches[mc].pkg_pepvpr
+ getRecipeVersions.readonly = True
+
+ def getRecipeProvides(self, command, params):
+ try:
+ mc = params[0]
+ except IndexError:
+ mc = ''
+ return command.cooker.recipecaches[mc].fn_provides
+ getRecipeProvides.readonly = True
+
+ def getRecipePackages(self, command, params):
+ try:
+ mc = params[0]
+ except IndexError:
+ mc = ''
+ return command.cooker.recipecaches[mc].packages
+ getRecipePackages.readonly = True
+
+ def getRecipePackagesDynamic(self, command, params):
+ try:
+ mc = params[0]
+ except IndexError:
+ mc = ''
+ return command.cooker.recipecaches[mc].packages_dynamic
+ getRecipePackagesDynamic.readonly = True
+
+ def getRProviders(self, command, params):
+ try:
+ mc = params[0]
+ except IndexError:
+ mc = ''
+ return command.cooker.recipecaches[mc].rproviders
+ getRProviders.readonly = True
+
+ def getRuntimeDepends(self, command, params):
+ ret = []
+ try:
+ mc = params[0]
+ except IndexError:
+ mc = ''
+ rundeps = command.cooker.recipecaches[mc].rundeps
+ for key, value in rundeps.items():
+ if isinstance(value, defaultdict):
+ value = dict(value)
+ ret.append((key, value))
+ return ret
+ getRuntimeDepends.readonly = True
+
+ def getRuntimeRecommends(self, command, params):
+ ret = []
+ try:
+ mc = params[0]
+ except IndexError:
+ mc = ''
+ runrecs = command.cooker.recipecaches[mc].runrecs
+ for key, value in runrecs.items():
+ if isinstance(value, defaultdict):
+ value = dict(value)
+ ret.append((key, value))
+ return ret
+ getRuntimeRecommends.readonly = True
+
+ def getRecipeInherits(self, command, params):
+ try:
+ mc = params[0]
+ except IndexError:
+ mc = ''
+ return command.cooker.recipecaches[mc].inherits
+ getRecipeInherits.readonly = True
+
+ def getBbFilePriority(self, command, params):
+ try:
+ mc = params[0]
+ except IndexError:
+ mc = ''
+ return command.cooker.recipecaches[mc].bbfile_priority
+ getBbFilePriority.readonly = True
+
+ def getDefaultPreference(self, command, params):
+ try:
+ mc = params[0]
+ except IndexError:
+ mc = ''
+ return command.cooker.recipecaches[mc].pkg_dp
+ getDefaultPreference.readonly = True
+
+ def getSkippedRecipes(self, command, params):
+ # Return list sorted by reverse priority order
+ import bb.cache
+ skipdict = OrderedDict(sorted(command.cooker.skiplist.items(),
+ key=lambda x: (-command.cooker.collection.calc_bbfile_priority(bb.cache.virtualfn2realfn(x[0])[0]), x[0])))
+ return list(skipdict.items())
+ getSkippedRecipes.readonly = True
+
+ def getOverlayedRecipes(self, command, params):
+ return list(command.cooker.collection.overlayed.items())
+ getOverlayedRecipes.readonly = True
+
+ def getFileAppends(self, command, params):
+ fn = params[0]
+ return command.cooker.collection.get_file_appends(fn)
+ getFileAppends.readonly = True
+
+ def getAllAppends(self, command, params):
+ return command.cooker.collection.bbappends
+ getAllAppends.readonly = True
+
+ def findProviders(self, command, params):
+ return command.cooker.findProviders()
+ findProviders.readonly = True
+
+ def findBestProvider(self, command, params):
+ (mc, pn) = split_mc_pn(params[0])
+ return command.cooker.findBestProvider(pn, mc)
+ findBestProvider.readonly = True
+
+ def allProviders(self, command, params):
+ try:
+ mc = params[0]
+ except IndexError:
+ mc = ''
+ return list(bb.providers.allProviders(command.cooker.recipecaches[mc]).items())
+ allProviders.readonly = True
+
+ def getRuntimeProviders(self, command, params):
+ rprovide = params[0]
+ try:
+ mc = params[1]
+ except IndexError:
+ mc = ''
+ all_p = bb.providers.getRuntimeProviders(command.cooker.recipecaches[mc], rprovide)
+ if all_p:
+ best = bb.providers.filterProvidersRunTime(all_p, rprovide,
+ command.cooker.data,
+ command.cooker.recipecaches[mc])[0][0]
+ else:
+ best = None
+ return all_p, best
+ getRuntimeProviders.readonly = True
+
+ def dataStoreConnectorFindVar(self, command, params):
+ dsindex = params[0]
+ name = params[1]
+ datastore = command.remotedatastores[dsindex]
+ value, overridedata = datastore._findVar(name)
+
+ if value:
+ content = value.get('_content', None)
+ if isinstance(content, bb.data_smart.DataSmart):
+ # Value is a datastore (e.g. BB_ORIGENV) - need to handle this carefully
+ idx = command.remotedatastores.check_store(content, True)
+ return {'_content': DataStoreConnectionHandle(idx),
+ '_connector_origtype': 'DataStoreConnectionHandle',
+ '_connector_overrides': overridedata}
+ elif isinstance(content, set):
+ return {'_content': list(content),
+ '_connector_origtype': 'set',
+ '_connector_overrides': overridedata}
+ else:
+ value['_connector_overrides'] = overridedata
+ else:
+ value = {}
+ value['_connector_overrides'] = overridedata
+ return value
+ dataStoreConnectorFindVar.readonly = True
+
+ def dataStoreConnectorGetKeys(self, command, params):
+ dsindex = params[0]
+ datastore = command.remotedatastores[dsindex]
+ return list(datastore.keys())
+ dataStoreConnectorGetKeys.readonly = True
+
+ def dataStoreConnectorGetVarHistory(self, command, params):
+ dsindex = params[0]
+ name = params[1]
+ datastore = command.remotedatastores[dsindex]
+ return datastore.varhistory.variable(name)
+ dataStoreConnectorGetVarHistory.readonly = True
+
+ def dataStoreConnectorExpandPythonRef(self, command, params):
+ config_data_dict = params[0]
+ varname = params[1]
+ expr = params[2]
+
+ config_data = command.remotedatastores.receive_datastore(config_data_dict)
+
+ varparse = bb.data_smart.VariableParse(varname, config_data)
+ return varparse.python_sub(expr)
+
+ def dataStoreConnectorRelease(self, command, params):
+ dsindex = params[0]
+ if dsindex <= 0:
+ raise CommandError('dataStoreConnectorRelease: invalid index %d' % dsindex)
+ command.remotedatastores.release(dsindex)
+
+ def dataStoreConnectorSetVarFlag(self, command, params):
+ dsindex = params[0]
+ name = params[1]
+ flag = params[2]
+ value = params[3]
+ datastore = command.remotedatastores[dsindex]
+ datastore.setVarFlag(name, flag, value)
+
+ def dataStoreConnectorDelVar(self, command, params):
+ dsindex = params[0]
+ name = params[1]
+ datastore = command.remotedatastores[dsindex]
+ if len(params) > 2:
+ flag = params[2]
+ datastore.delVarFlag(name, flag)
+ else:
+ datastore.delVar(name)
+
+ def dataStoreConnectorRenameVar(self, command, params):
+ dsindex = params[0]
+ name = params[1]
+ newname = params[2]
+ datastore = command.remotedatastores[dsindex]
+ datastore.renameVar(name, newname)
+
+ def parseRecipeFile(self, command, params):
+ """
+ Parse the specified recipe file (with or without bbappends)
+ and return a datastore object representing the environment
+ for the recipe.
+ """
+ fn = params[0]
+ appends = params[1]
+ appendlist = params[2]
+ if len(params) > 3:
+ config_data_dict = params[3]
+ config_data = command.remotedatastores.receive_datastore(config_data_dict)
+ else:
+ config_data = None
+
+ if appends:
+ if appendlist is not None:
+ appendfiles = appendlist
+ else:
+ appendfiles = command.cooker.collection.get_file_appends(fn)
+ else:
+ appendfiles = []
+ # We are calling bb.cache locally here rather than on the server,
+ # but that's OK because it doesn't actually need anything from
+ # the server barring the global datastore (which we have a remote
+ # version of)
+ if config_data:
+ # We have to use a different function here if we're passing in a datastore
+ # NOTE: we took a copy above, so we don't do it here again
+ envdata = bb.cache.parse_recipe(config_data, fn, appendfiles)['']
+ else:
+ # Use the standard path
+ parser = bb.cache.NoCache(command.cooker.databuilder)
+ envdata = parser.loadDataFull(fn, appendfiles)
+ idx = command.remotedatastores.store(envdata)
+ return DataStoreConnectionHandle(idx)
+ parseRecipeFile.readonly = True
+
+class CommandsAsync:
+ """
+ A class of asynchronous commands
+ These functions communicate via generated events.
+ Any function that requires metadata parsing should be here.
+ """
+
+ def buildFile(self, command, params):
+ """
+ Build a single specified .bb file
+ """
+ bfile = params[0]
+ task = params[1]
+ if len(params) > 2:
+ internal = params[2]
+ else:
+ internal = False
+
+ if internal:
+ command.cooker.buildFileInternal(bfile, task, fireevents=False, quietlog=True)
+ else:
+ command.cooker.buildFile(bfile, task)
+ buildFile.needcache = False
+
+ def buildTargets(self, command, params):
+ """
+ Build a set of targets
+ """
+ pkgs_to_build = params[0]
+ task = params[1]
+
+ command.cooker.buildTargets(pkgs_to_build, task)
+ buildTargets.needcache = True
+
+ def generateDepTreeEvent(self, command, params):
+ """
+ Generate an event containing the dependency information
+ """
+ pkgs_to_build = params[0]
+ task = params[1]
+
+ command.cooker.generateDepTreeEvent(pkgs_to_build, task)
+ command.finishAsyncCommand()
+ generateDepTreeEvent.needcache = True
+
+ def generateDotGraph(self, command, params):
+ """
+ Dump dependency information to disk as .dot files
+ """
+ pkgs_to_build = params[0]
+ task = params[1]
+
+ command.cooker.generateDotGraphFiles(pkgs_to_build, task)
+ command.finishAsyncCommand()
+ generateDotGraph.needcache = True
+
+ def generateTargetsTree(self, command, params):
+ """
+ Generate a tree of buildable targets.
+ If klass is provided ensure all recipes that inherit the class are
+ included in the package list.
+ If pkg_list provided use that list (plus any extras brought in by
+ klass) rather than generating a tree for all packages.
+ """
+ klass = params[0]
+ pkg_list = params[1]
+
+ command.cooker.generateTargetsTree(klass, pkg_list)
+ command.finishAsyncCommand()
+ generateTargetsTree.needcache = True
+
+ def findConfigFiles(self, command, params):
+ """
+ Find config files which provide appropriate values
+ for the passed configuration variable. i.e. MACHINE
+ """
+ varname = params[0]
+
+ command.cooker.findConfigFiles(varname)
+ command.finishAsyncCommand()
+ findConfigFiles.needcache = False
+
+ def findFilesMatchingInDir(self, command, params):
+ """
+ Find implementation files matching the specified pattern
+ in the requested subdirectory of a BBPATH
+ """
+ pattern = params[0]
+ directory = params[1]
+
+ command.cooker.findFilesMatchingInDir(pattern, directory)
+ command.finishAsyncCommand()
+ findFilesMatchingInDir.needcache = False
+
+ def findConfigFilePath(self, command, params):
+ """
+ Find the path of the requested configuration file
+ """
+ configfile = params[0]
+
+ command.cooker.findConfigFilePath(configfile)
+ command.finishAsyncCommand()
+ findConfigFilePath.needcache = False
+
+ def showVersions(self, command, params):
+ """
+ Show the currently selected versions
+ """
+ command.cooker.showVersions()
+ command.finishAsyncCommand()
+ showVersions.needcache = True
+
+ def showEnvironmentTarget(self, command, params):
+ """
+ Print the environment of a target recipe
+ (needs the cache to work out which recipe to use)
+ """
+ pkg = params[0]
+
+ command.cooker.showEnvironment(None, pkg)
+ command.finishAsyncCommand()
+ showEnvironmentTarget.needcache = True
+
+ def showEnvironment(self, command, params):
+ """
+ Print the standard environment
+ or if specified the environment for a specified recipe
+ """
+ bfile = params[0]
+
+ command.cooker.showEnvironment(bfile)
+ command.finishAsyncCommand()
+ showEnvironment.needcache = False
+
+ def parseFiles(self, command, params):
+ """
+ Parse the .bb files
+ """
+ command.cooker.updateCache()
+ command.finishAsyncCommand()
+ parseFiles.needcache = True
+
+ def compareRevisions(self, command, params):
+ """
+ Parse the .bb files
+ """
+ if bb.fetch.fetcher_compare_revisions(command.cooker.data):
+ command.finishAsyncCommand(code=1)
+ else:
+ command.finishAsyncCommand()
+ compareRevisions.needcache = True
+
+ def triggerEvent(self, command, params):
+ """
+ Trigger a certain event
+ """
+ event = params[0]
+ bb.event.fire(eval(event), command.cooker.data)
+ command.currentAsyncCommand = None
+ triggerEvent.needcache = False
+
+ def resetCooker(self, command, params):
+ """
+ Reset the cooker to its initial state, thus forcing a reparse for
+ any async command that has the needcache property set to True
+ """
+ command.cooker.reset()
+ command.finishAsyncCommand()
+ resetCooker.needcache = False
+
+ def clientComplete(self, command, params):
+ """
+ Do the right thing when the controlling client exits
+ """
+ command.cooker.clientComplete()
+ command.finishAsyncCommand()
+ clientComplete.needcache = False
+
+ def findSigInfo(self, command, params):
+ """
+ Find signature info files via the signature generator
+ """
+ pn = params[0]
+ taskname = params[1]
+ sigs = params[2]
+ res = bb.siggen.find_siginfo(pn, taskname, sigs, command.cooker.data)
+ bb.event.fire(bb.event.FindSigInfoResult(res), command.cooker.data)
+ command.finishAsyncCommand()
+ findSigInfo.needcache = False