command.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744
  1. """
  2. BitBake 'Command' module
  3. Provide an interface to interact with the bitbake server through 'commands'
  4. """
  5. # Copyright (C) 2006-2007 Richard Purdie
  6. #
  7. # SPDX-License-Identifier: GPL-2.0-only
  8. #
  9. """
  10. The bitbake server takes 'commands' from its UI/commandline.
  11. Commands are either synchronous or asynchronous.
  12. Async commands return data to the client in the form of events.
  13. Sync commands must only return data through the function return value
  14. and must not trigger events, directly or indirectly.
  15. Commands are queued in a CommandQueue
  16. """
  17. from collections import OrderedDict, defaultdict
  18. import bb.event
  19. import bb.cooker
  20. import bb.remotedata
  21. class DataStoreConnectionHandle(object):
  22. def __init__(self, dsindex=0):
  23. self.dsindex = dsindex
  24. class CommandCompleted(bb.event.Event):
  25. pass
  26. class CommandExit(bb.event.Event):
  27. def __init__(self, exitcode):
  28. bb.event.Event.__init__(self)
  29. self.exitcode = int(exitcode)
  30. class CommandFailed(CommandExit):
  31. def __init__(self, message):
  32. self.error = message
  33. CommandExit.__init__(self, 1)
  34. def __str__(self):
  35. return "Command execution failed: %s" % self.error
  36. class CommandError(Exception):
  37. pass
  38. class Command:
  39. """
  40. A queue of asynchronous commands for bitbake
  41. """
  42. def __init__(self, cooker):
  43. self.cooker = cooker
  44. self.cmds_sync = CommandsSync()
  45. self.cmds_async = CommandsAsync()
  46. self.remotedatastores = None
  47. # FIXME Add lock for this
  48. self.currentAsyncCommand = None
  49. def runCommand(self, commandline, ro_only = False):
  50. command = commandline.pop(0)
  51. # Ensure cooker is ready for commands
  52. if command != "updateConfig" and command != "setFeatures":
  53. self.cooker.init_configdata()
  54. if not self.remotedatastores:
  55. self.remotedatastores = bb.remotedata.RemoteDatastores(self.cooker)
  56. if hasattr(CommandsSync, command):
  57. # Can run synchronous commands straight away
  58. command_method = getattr(self.cmds_sync, command)
  59. if ro_only:
  60. if not hasattr(command_method, 'readonly') or not getattr(command_method, 'readonly'):
  61. return None, "Not able to execute not readonly commands in readonly mode"
  62. try:
  63. self.cooker.process_inotify_updates()
  64. if getattr(command_method, 'needconfig', True):
  65. self.cooker.updateCacheSync()
  66. result = command_method(self, commandline)
  67. except CommandError as exc:
  68. return None, exc.args[0]
  69. except (Exception, SystemExit) as exc:
  70. import traceback
  71. if isinstance(exc, bb.BBHandledException):
  72. # We need to start returning real exceptions here. Until we do, we can't
  73. # tell if an exception is an instance of bb.BBHandledException
  74. return None, "bb.BBHandledException()\n" + traceback.format_exc()
  75. return None, traceback.format_exc()
  76. else:
  77. return result, None
  78. if self.currentAsyncCommand is not None:
  79. return None, "Busy (%s in progress)" % self.currentAsyncCommand[0]
  80. if command not in CommandsAsync.__dict__:
  81. return None, "No such command"
  82. self.currentAsyncCommand = (command, commandline)
  83. self.cooker.idleCallBackRegister(self.cooker.runCommands, self.cooker)
  84. return True, None
  85. def runAsyncCommand(self):
  86. try:
  87. self.cooker.process_inotify_updates()
  88. if self.cooker.state in (bb.cooker.state.error, bb.cooker.state.shutdown, bb.cooker.state.forceshutdown):
  89. # updateCache will trigger a shutdown of the parser
  90. # and then raise BBHandledException triggering an exit
  91. self.cooker.updateCache()
  92. return False
  93. if self.currentAsyncCommand is not None:
  94. (command, options) = self.currentAsyncCommand
  95. commandmethod = getattr(CommandsAsync, command)
  96. needcache = getattr( commandmethod, "needcache" )
  97. if needcache and self.cooker.state != bb.cooker.state.running:
  98. self.cooker.updateCache()
  99. return True
  100. else:
  101. commandmethod(self.cmds_async, self, options)
  102. return False
  103. else:
  104. return False
  105. except KeyboardInterrupt as exc:
  106. self.finishAsyncCommand("Interrupted")
  107. return False
  108. except SystemExit as exc:
  109. arg = exc.args[0]
  110. if isinstance(arg, str):
  111. self.finishAsyncCommand(arg)
  112. else:
  113. self.finishAsyncCommand("Exited with %s" % arg)
  114. return False
  115. except Exception as exc:
  116. import traceback
  117. if isinstance(exc, bb.BBHandledException):
  118. self.finishAsyncCommand("")
  119. else:
  120. self.finishAsyncCommand(traceback.format_exc())
  121. return False
  122. def finishAsyncCommand(self, msg=None, code=None):
  123. if msg or msg == "":
  124. bb.event.fire(CommandFailed(msg), self.cooker.data)
  125. elif code:
  126. bb.event.fire(CommandExit(code), self.cooker.data)
  127. else:
  128. bb.event.fire(CommandCompleted(), self.cooker.data)
  129. self.currentAsyncCommand = None
  130. self.cooker.finishcommand()
  131. def reset(self):
  132. if self.remotedatastores:
  133. self.remotedatastores = bb.remotedata.RemoteDatastores(self.cooker)
  134. class CommandsSync:
  135. """
  136. A class of synchronous commands
  137. These should run quickly so as not to hurt interactive performance.
  138. These must not influence any running synchronous command.
  139. """
  140. def stateShutdown(self, command, params):
  141. """
  142. Trigger cooker 'shutdown' mode
  143. """
  144. command.cooker.shutdown(False)
  145. def stateForceShutdown(self, command, params):
  146. """
  147. Stop the cooker
  148. """
  149. command.cooker.shutdown(True)
  150. def getAllKeysWithFlags(self, command, params):
  151. """
  152. Returns a dump of the global state. Call with
  153. variable flags to be retrieved as params.
  154. """
  155. flaglist = params[0]
  156. return command.cooker.getAllKeysWithFlags(flaglist)
  157. getAllKeysWithFlags.readonly = True
  158. def getVariable(self, command, params):
  159. """
  160. Read the value of a variable from data
  161. """
  162. varname = params[0]
  163. expand = True
  164. if len(params) > 1:
  165. expand = (params[1] == "True")
  166. return command.cooker.data.getVar(varname, expand)
  167. getVariable.readonly = True
  168. def setVariable(self, command, params):
  169. """
  170. Set the value of variable in data
  171. """
  172. varname = params[0]
  173. value = str(params[1])
  174. command.cooker.extraconfigdata[varname] = value
  175. command.cooker.data.setVar(varname, value)
  176. def getSetVariable(self, command, params):
  177. """
  178. Read the value of a variable from data and set it into the datastore
  179. which effectively expands and locks the value.
  180. """
  181. varname = params[0]
  182. result = self.getVariable(command, params)
  183. command.cooker.data.setVar(varname, result)
  184. return result
  185. def setConfig(self, command, params):
  186. """
  187. Set the value of variable in configuration
  188. """
  189. varname = params[0]
  190. value = str(params[1])
  191. setattr(command.cooker.configuration, varname, value)
  192. def enableDataTracking(self, command, params):
  193. """
  194. Enable history tracking for variables
  195. """
  196. command.cooker.enableDataTracking()
  197. def disableDataTracking(self, command, params):
  198. """
  199. Disable history tracking for variables
  200. """
  201. command.cooker.disableDataTracking()
  202. def setPrePostConfFiles(self, command, params):
  203. prefiles = params[0].split()
  204. postfiles = params[1].split()
  205. command.cooker.configuration.prefile = prefiles
  206. command.cooker.configuration.postfile = postfiles
  207. setPrePostConfFiles.needconfig = False
  208. def matchFile(self, command, params):
  209. fMatch = params[0]
  210. try:
  211. mc = params[0]
  212. except IndexError:
  213. mc = ''
  214. return command.cooker.matchFile(fMatch, mc)
  215. matchFile.needconfig = False
  216. def getUIHandlerNum(self, command, params):
  217. return bb.event.get_uihandler()
  218. getUIHandlerNum.needconfig = False
  219. getUIHandlerNum.readonly = True
  220. def setEventMask(self, command, params):
  221. handlerNum = params[0]
  222. llevel = params[1]
  223. debug_domains = params[2]
  224. mask = params[3]
  225. return bb.event.set_UIHmask(handlerNum, llevel, debug_domains, mask)
  226. setEventMask.needconfig = False
  227. setEventMask.readonly = True
  228. def setFeatures(self, command, params):
  229. """
  230. Set the cooker features to include the passed list of features
  231. """
  232. features = params[0]
  233. command.cooker.setFeatures(features)
  234. setFeatures.needconfig = False
  235. # although we change the internal state of the cooker, this is transparent since
  236. # we always take and leave the cooker in state.initial
  237. setFeatures.readonly = True
  238. def updateConfig(self, command, params):
  239. options = params[0]
  240. environment = params[1]
  241. cmdline = params[2]
  242. command.cooker.updateConfigOpts(options, environment, cmdline)
  243. updateConfig.needconfig = False
  244. def parseConfiguration(self, command, params):
  245. """Instruct bitbake to parse its configuration
  246. NOTE: it is only necessary to call this if you aren't calling any normal action
  247. (otherwise parsing is taken care of automatically)
  248. """
  249. command.cooker.parseConfiguration()
  250. parseConfiguration.needconfig = False
  251. def getLayerPriorities(self, command, params):
  252. command.cooker.parseConfiguration()
  253. ret = []
  254. # regex objects cannot be marshalled by xmlrpc
  255. for collection, pattern, regex, pri in command.cooker.bbfile_config_priorities:
  256. ret.append((collection, pattern, regex.pattern, pri))
  257. return ret
  258. getLayerPriorities.readonly = True
  259. def getRecipes(self, command, params):
  260. try:
  261. mc = params[0]
  262. except IndexError:
  263. mc = ''
  264. return list(command.cooker.recipecaches[mc].pkg_pn.items())
  265. getRecipes.readonly = True
  266. def getRecipeDepends(self, command, params):
  267. try:
  268. mc = params[0]
  269. except IndexError:
  270. mc = ''
  271. return list(command.cooker.recipecaches[mc].deps.items())
  272. getRecipeDepends.readonly = True
  273. def getRecipeVersions(self, command, params):
  274. try:
  275. mc = params[0]
  276. except IndexError:
  277. mc = ''
  278. return command.cooker.recipecaches[mc].pkg_pepvpr
  279. getRecipeVersions.readonly = True
  280. def getRecipeProvides(self, command, params):
  281. try:
  282. mc = params[0]
  283. except IndexError:
  284. mc = ''
  285. return command.cooker.recipecaches[mc].fn_provides
  286. getRecipeProvides.readonly = True
  287. def getRecipePackages(self, command, params):
  288. try:
  289. mc = params[0]
  290. except IndexError:
  291. mc = ''
  292. return command.cooker.recipecaches[mc].packages
  293. getRecipePackages.readonly = True
  294. def getRecipePackagesDynamic(self, command, params):
  295. try:
  296. mc = params[0]
  297. except IndexError:
  298. mc = ''
  299. return command.cooker.recipecaches[mc].packages_dynamic
  300. getRecipePackagesDynamic.readonly = True
  301. def getRProviders(self, command, params):
  302. try:
  303. mc = params[0]
  304. except IndexError:
  305. mc = ''
  306. return command.cooker.recipecaches[mc].rproviders
  307. getRProviders.readonly = True
  308. def getRuntimeDepends(self, command, params):
  309. ret = []
  310. try:
  311. mc = params[0]
  312. except IndexError:
  313. mc = ''
  314. rundeps = command.cooker.recipecaches[mc].rundeps
  315. for key, value in rundeps.items():
  316. if isinstance(value, defaultdict):
  317. value = dict(value)
  318. ret.append((key, value))
  319. return ret
  320. getRuntimeDepends.readonly = True
  321. def getRuntimeRecommends(self, command, params):
  322. ret = []
  323. try:
  324. mc = params[0]
  325. except IndexError:
  326. mc = ''
  327. runrecs = command.cooker.recipecaches[mc].runrecs
  328. for key, value in runrecs.items():
  329. if isinstance(value, defaultdict):
  330. value = dict(value)
  331. ret.append((key, value))
  332. return ret
  333. getRuntimeRecommends.readonly = True
  334. def getRecipeInherits(self, command, params):
  335. try:
  336. mc = params[0]
  337. except IndexError:
  338. mc = ''
  339. return command.cooker.recipecaches[mc].inherits
  340. getRecipeInherits.readonly = True
  341. def getBbFilePriority(self, command, params):
  342. try:
  343. mc = params[0]
  344. except IndexError:
  345. mc = ''
  346. return command.cooker.recipecaches[mc].bbfile_priority
  347. getBbFilePriority.readonly = True
  348. def getDefaultPreference(self, command, params):
  349. try:
  350. mc = params[0]
  351. except IndexError:
  352. mc = ''
  353. return command.cooker.recipecaches[mc].pkg_dp
  354. getDefaultPreference.readonly = True
  355. def getSkippedRecipes(self, command, params):
  356. # Return list sorted by reverse priority order
  357. import bb.cache
  358. def sortkey(x):
  359. vfn, _ = x
  360. realfn, _, mc = bb.cache.virtualfn2realfn(vfn)
  361. return (-command.cooker.collections[mc].calc_bbfile_priority(realfn)[0], vfn)
  362. skipdict = OrderedDict(sorted(command.cooker.skiplist.items(), key=sortkey))
  363. return list(skipdict.items())
  364. getSkippedRecipes.readonly = True
  365. def getOverlayedRecipes(self, command, params):
  366. try:
  367. mc = params[0]
  368. except IndexError:
  369. mc = ''
  370. return list(command.cooker.collections[mc].overlayed.items())
  371. getOverlayedRecipes.readonly = True
  372. def getFileAppends(self, command, params):
  373. fn = params[0]
  374. try:
  375. mc = params[1]
  376. except IndexError:
  377. mc = ''
  378. return command.cooker.collections[mc].get_file_appends(fn)
  379. getFileAppends.readonly = True
  380. def getAllAppends(self, command, params):
  381. try:
  382. mc = params[0]
  383. except IndexError:
  384. mc = ''
  385. return command.cooker.collections[mc].bbappends
  386. getAllAppends.readonly = True
  387. def findProviders(self, command, params):
  388. try:
  389. mc = params[0]
  390. except IndexError:
  391. mc = ''
  392. return command.cooker.findProviders(mc)
  393. findProviders.readonly = True
  394. def findBestProvider(self, command, params):
  395. (mc, pn) = bb.runqueue.split_mc(params[0])
  396. return command.cooker.findBestProvider(pn, mc)
  397. findBestProvider.readonly = True
  398. def allProviders(self, command, params):
  399. try:
  400. mc = params[0]
  401. except IndexError:
  402. mc = ''
  403. return list(bb.providers.allProviders(command.cooker.recipecaches[mc]).items())
  404. allProviders.readonly = True
  405. def getRuntimeProviders(self, command, params):
  406. rprovide = params[0]
  407. try:
  408. mc = params[1]
  409. except IndexError:
  410. mc = ''
  411. all_p = bb.providers.getRuntimeProviders(command.cooker.recipecaches[mc], rprovide)
  412. if all_p:
  413. best = bb.providers.filterProvidersRunTime(all_p, rprovide,
  414. command.cooker.data,
  415. command.cooker.recipecaches[mc])[0][0]
  416. else:
  417. best = None
  418. return all_p, best
  419. getRuntimeProviders.readonly = True
  420. def dataStoreConnectorCmd(self, command, params):
  421. dsindex = params[0]
  422. method = params[1]
  423. args = params[2]
  424. kwargs = params[3]
  425. d = command.remotedatastores[dsindex]
  426. ret = getattr(d, method)(*args, **kwargs)
  427. if isinstance(ret, bb.data_smart.DataSmart):
  428. idx = command.remotedatastores.store(ret)
  429. return DataStoreConnectionHandle(idx)
  430. return ret
  431. def dataStoreConnectorVarHistCmd(self, command, params):
  432. dsindex = params[0]
  433. method = params[1]
  434. args = params[2]
  435. kwargs = params[3]
  436. d = command.remotedatastores[dsindex].varhistory
  437. return getattr(d, method)(*args, **kwargs)
  438. def dataStoreConnectorIncHistCmd(self, command, params):
  439. dsindex = params[0]
  440. method = params[1]
  441. args = params[2]
  442. kwargs = params[3]
  443. d = command.remotedatastores[dsindex].inchistory
  444. return getattr(d, method)(*args, **kwargs)
  445. def dataStoreConnectorRelease(self, command, params):
  446. dsindex = params[0]
  447. if dsindex <= 0:
  448. raise CommandError('dataStoreConnectorRelease: invalid index %d' % dsindex)
  449. command.remotedatastores.release(dsindex)
  450. def parseRecipeFile(self, command, params):
  451. """
  452. Parse the specified recipe file (with or without bbappends)
  453. and return a datastore object representing the environment
  454. for the recipe.
  455. """
  456. fn = params[0]
  457. mc = bb.runqueue.mc_from_tid(fn)
  458. appends = params[1]
  459. appendlist = params[2]
  460. if len(params) > 3:
  461. config_data = command.remotedatastores[params[3]]
  462. else:
  463. config_data = None
  464. if appends:
  465. if appendlist is not None:
  466. appendfiles = appendlist
  467. else:
  468. appendfiles = command.cooker.collections[mc].get_file_appends(fn)
  469. else:
  470. appendfiles = []
  471. # We are calling bb.cache locally here rather than on the server,
  472. # but that's OK because it doesn't actually need anything from
  473. # the server barring the global datastore (which we have a remote
  474. # version of)
  475. if config_data:
  476. # We have to use a different function here if we're passing in a datastore
  477. # NOTE: we took a copy above, so we don't do it here again
  478. envdata = bb.cache.parse_recipe(config_data, fn, appendfiles, mc)['']
  479. else:
  480. # Use the standard path
  481. parser = bb.cache.NoCache(command.cooker.databuilder)
  482. envdata = parser.loadDataFull(fn, appendfiles)
  483. idx = command.remotedatastores.store(envdata)
  484. return DataStoreConnectionHandle(idx)
  485. parseRecipeFile.readonly = True
  486. class CommandsAsync:
  487. """
  488. A class of asynchronous commands
  489. These functions communicate via generated events.
  490. Any function that requires metadata parsing should be here.
  491. """
  492. def buildFile(self, command, params):
  493. """
  494. Build a single specified .bb file
  495. """
  496. bfile = params[0]
  497. task = params[1]
  498. if len(params) > 2:
  499. internal = params[2]
  500. else:
  501. internal = False
  502. if internal:
  503. command.cooker.buildFileInternal(bfile, task, fireevents=False, quietlog=True)
  504. else:
  505. command.cooker.buildFile(bfile, task)
  506. buildFile.needcache = False
  507. def buildTargets(self, command, params):
  508. """
  509. Build a set of targets
  510. """
  511. pkgs_to_build = params[0]
  512. task = params[1]
  513. command.cooker.buildTargets(pkgs_to_build, task)
  514. buildTargets.needcache = True
  515. def generateDepTreeEvent(self, command, params):
  516. """
  517. Generate an event containing the dependency information
  518. """
  519. pkgs_to_build = params[0]
  520. task = params[1]
  521. command.cooker.generateDepTreeEvent(pkgs_to_build, task)
  522. command.finishAsyncCommand()
  523. generateDepTreeEvent.needcache = True
  524. def generateDotGraph(self, command, params):
  525. """
  526. Dump dependency information to disk as .dot files
  527. """
  528. pkgs_to_build = params[0]
  529. task = params[1]
  530. command.cooker.generateDotGraphFiles(pkgs_to_build, task)
  531. command.finishAsyncCommand()
  532. generateDotGraph.needcache = True
  533. def generateTargetsTree(self, command, params):
  534. """
  535. Generate a tree of buildable targets.
  536. If klass is provided ensure all recipes that inherit the class are
  537. included in the package list.
  538. If pkg_list provided use that list (plus any extras brought in by
  539. klass) rather than generating a tree for all packages.
  540. """
  541. klass = params[0]
  542. pkg_list = params[1]
  543. command.cooker.generateTargetsTree(klass, pkg_list)
  544. command.finishAsyncCommand()
  545. generateTargetsTree.needcache = True
  546. def findConfigFiles(self, command, params):
  547. """
  548. Find config files which provide appropriate values
  549. for the passed configuration variable. i.e. MACHINE
  550. """
  551. varname = params[0]
  552. command.cooker.findConfigFiles(varname)
  553. command.finishAsyncCommand()
  554. findConfigFiles.needcache = False
  555. def findFilesMatchingInDir(self, command, params):
  556. """
  557. Find implementation files matching the specified pattern
  558. in the requested subdirectory of a BBPATH
  559. """
  560. pattern = params[0]
  561. directory = params[1]
  562. command.cooker.findFilesMatchingInDir(pattern, directory)
  563. command.finishAsyncCommand()
  564. findFilesMatchingInDir.needcache = False
  565. def findConfigFilePath(self, command, params):
  566. """
  567. Find the path of the requested configuration file
  568. """
  569. configfile = params[0]
  570. command.cooker.findConfigFilePath(configfile)
  571. command.finishAsyncCommand()
  572. findConfigFilePath.needcache = False
  573. def showVersions(self, command, params):
  574. """
  575. Show the currently selected versions
  576. """
  577. command.cooker.showVersions()
  578. command.finishAsyncCommand()
  579. showVersions.needcache = True
  580. def showEnvironmentTarget(self, command, params):
  581. """
  582. Print the environment of a target recipe
  583. (needs the cache to work out which recipe to use)
  584. """
  585. pkg = params[0]
  586. command.cooker.showEnvironment(None, pkg)
  587. command.finishAsyncCommand()
  588. showEnvironmentTarget.needcache = True
  589. def showEnvironment(self, command, params):
  590. """
  591. Print the standard environment
  592. or if specified the environment for a specified recipe
  593. """
  594. bfile = params[0]
  595. command.cooker.showEnvironment(bfile)
  596. command.finishAsyncCommand()
  597. showEnvironment.needcache = False
  598. def parseFiles(self, command, params):
  599. """
  600. Parse the .bb files
  601. """
  602. command.cooker.updateCache()
  603. command.finishAsyncCommand()
  604. parseFiles.needcache = True
  605. def compareRevisions(self, command, params):
  606. """
  607. Parse the .bb files
  608. """
  609. if bb.fetch.fetcher_compare_revisions(command.cooker.data):
  610. command.finishAsyncCommand(code=1)
  611. else:
  612. command.finishAsyncCommand()
  613. compareRevisions.needcache = True
  614. def triggerEvent(self, command, params):
  615. """
  616. Trigger a certain event
  617. """
  618. event = params[0]
  619. bb.event.fire(eval(event), command.cooker.data)
  620. command.currentAsyncCommand = None
  621. triggerEvent.needcache = False
  622. def resetCooker(self, command, params):
  623. """
  624. Reset the cooker to its initial state, thus forcing a reparse for
  625. any async command that has the needcache property set to True
  626. """
  627. command.cooker.reset()
  628. command.finishAsyncCommand()
  629. resetCooker.needcache = False
  630. def clientComplete(self, command, params):
  631. """
  632. Do the right thing when the controlling client exits
  633. """
  634. command.cooker.clientComplete()
  635. command.finishAsyncCommand()
  636. clientComplete.needcache = False
  637. def findSigInfo(self, command, params):
  638. """
  639. Find signature info files via the signature generator
  640. """
  641. (mc, pn) = bb.runqueue.split_mc(params[0])
  642. taskname = params[1]
  643. sigs = params[2]
  644. res = bb.siggen.find_siginfo(pn, taskname, sigs, command.cooker.databuilder.mcdata[mc])
  645. bb.event.fire(bb.event.FindSigInfoResult(res), command.cooker.databuilder.mcdata[mc])
  646. command.finishAsyncCommand()
  647. findSigInfo.needcache = False