blob: a919f58d2488b4710a10abe09688c14f64c9bc11 [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001"""
2BitBake 'Command' module
3
4Provide an interface to interact with the bitbake server through 'commands'
5"""
6
7# Copyright (C) 2006-2007 Richard Purdie
8#
9# This program is free software; you can redistribute it and/or modify
10# it under the terms of the GNU General Public License version 2 as
11# published by the Free Software Foundation.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License along
19# with this program; if not, write to the Free Software Foundation, Inc.,
20# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21
22"""
23The bitbake server takes 'commands' from its UI/commandline.
24Commands are either synchronous or asynchronous.
25Async commands return data to the client in the form of events.
26Sync commands must only return data through the function return value
27and must not trigger events, directly or indirectly.
28Commands are queued in a CommandQueue
29"""
30
Brad Bishop6e60e8b2018-02-01 10:27:11 -050031from collections import OrderedDict, defaultdict
32
Patrick Williamsc124f4f2015-09-15 14:41:29 -050033import bb.event
34import bb.cooker
Brad Bishop6e60e8b2018-02-01 10:27:11 -050035import bb.remotedata
36
37class DataStoreConnectionHandle(object):
38 def __init__(self, dsindex=0):
39 self.dsindex = dsindex
Patrick Williamsc124f4f2015-09-15 14:41:29 -050040
41class CommandCompleted(bb.event.Event):
42 pass
43
44class CommandExit(bb.event.Event):
45 def __init__(self, exitcode):
46 bb.event.Event.__init__(self)
47 self.exitcode = int(exitcode)
48
49class CommandFailed(CommandExit):
50 def __init__(self, message):
51 self.error = message
52 CommandExit.__init__(self, 1)
53
54class CommandError(Exception):
55 pass
56
57class Command:
58 """
59 A queue of asynchronous commands for bitbake
60 """
61 def __init__(self, cooker):
62 self.cooker = cooker
63 self.cmds_sync = CommandsSync()
64 self.cmds_async = CommandsAsync()
Brad Bishop6e60e8b2018-02-01 10:27:11 -050065 self.remotedatastores = bb.remotedata.RemoteDatastores(cooker)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050066
67 # FIXME Add lock for this
68 self.currentAsyncCommand = None
69
70 def runCommand(self, commandline, ro_only = False):
71 command = commandline.pop(0)
72 if hasattr(CommandsSync, command):
73 # Can run synchronous commands straight away
74 command_method = getattr(self.cmds_sync, command)
75 if ro_only:
76 if not hasattr(command_method, 'readonly') or False == getattr(command_method, 'readonly'):
77 return None, "Not able to execute not readonly commands in readonly mode"
78 try:
79 if getattr(command_method, 'needconfig', False):
80 self.cooker.updateCacheSync()
81 result = command_method(self, commandline)
82 except CommandError as exc:
83 return None, exc.args[0]
84 except (Exception, SystemExit):
85 import traceback
86 return None, traceback.format_exc()
87 else:
88 return result, None
89 if self.currentAsyncCommand is not None:
90 return None, "Busy (%s in progress)" % self.currentAsyncCommand[0]
91 if command not in CommandsAsync.__dict__:
92 return None, "No such command"
93 self.currentAsyncCommand = (command, commandline)
94 self.cooker.configuration.server_register_idlecallback(self.cooker.runCommands, self.cooker)
95 return True, None
96
97 def runAsyncCommand(self):
98 try:
99 if self.cooker.state in (bb.cooker.state.error, bb.cooker.state.shutdown, bb.cooker.state.forceshutdown):
100 # updateCache will trigger a shutdown of the parser
101 # and then raise BBHandledException triggering an exit
102 self.cooker.updateCache()
103 return False
104 if self.currentAsyncCommand is not None:
105 (command, options) = self.currentAsyncCommand
106 commandmethod = getattr(CommandsAsync, command)
107 needcache = getattr( commandmethod, "needcache" )
108 if needcache and self.cooker.state != bb.cooker.state.running:
109 self.cooker.updateCache()
110 return True
111 else:
112 commandmethod(self.cmds_async, self, options)
113 return False
114 else:
115 return False
116 except KeyboardInterrupt as exc:
117 self.finishAsyncCommand("Interrupted")
118 return False
119 except SystemExit as exc:
120 arg = exc.args[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600121 if isinstance(arg, str):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500122 self.finishAsyncCommand(arg)
123 else:
124 self.finishAsyncCommand("Exited with %s" % arg)
125 return False
126 except Exception as exc:
127 import traceback
128 if isinstance(exc, bb.BBHandledException):
129 self.finishAsyncCommand("")
130 else:
131 self.finishAsyncCommand(traceback.format_exc())
132 return False
133
134 def finishAsyncCommand(self, msg=None, code=None):
135 if msg or msg == "":
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500136 bb.event.fire(CommandFailed(msg), self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500137 elif code:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500138 bb.event.fire(CommandExit(code), self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500139 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500140 bb.event.fire(CommandCompleted(), self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500141 self.currentAsyncCommand = None
142 self.cooker.finishcommand()
143
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500144def split_mc_pn(pn):
145 if pn.startswith("multiconfig:"):
146 _, mc, pn = pn.split(":", 2)
147 return (mc, pn)
148 return ('', pn)
149
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500150class CommandsSync:
151 """
152 A class of synchronous commands
153 These should run quickly so as not to hurt interactive performance.
154 These must not influence any running synchronous command.
155 """
156
157 def stateShutdown(self, command, params):
158 """
159 Trigger cooker 'shutdown' mode
160 """
161 command.cooker.shutdown(False)
162
163 def stateForceShutdown(self, command, params):
164 """
165 Stop the cooker
166 """
167 command.cooker.shutdown(True)
168
169 def getAllKeysWithFlags(self, command, params):
170 """
171 Returns a dump of the global state. Call with
172 variable flags to be retrieved as params.
173 """
174 flaglist = params[0]
175 return command.cooker.getAllKeysWithFlags(flaglist)
176 getAllKeysWithFlags.readonly = True
177
178 def getVariable(self, command, params):
179 """
180 Read the value of a variable from data
181 """
182 varname = params[0]
183 expand = True
184 if len(params) > 1:
185 expand = (params[1] == "True")
186
187 return command.cooker.data.getVar(varname, expand)
188 getVariable.readonly = True
189
190 def setVariable(self, command, params):
191 """
192 Set the value of variable in data
193 """
194 varname = params[0]
195 value = str(params[1])
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500196 command.cooker.extraconfigdata[varname] = value
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500197 command.cooker.data.setVar(varname, value)
198
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500199 def getSetVariable(self, command, params):
200 """
201 Read the value of a variable from data and set it into the datastore
202 which effectively expands and locks the value.
203 """
204 varname = params[0]
205 result = self.getVariable(command, params)
206 command.cooker.data.setVar(varname, result)
207 return result
208
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500209 def setConfig(self, command, params):
210 """
211 Set the value of variable in configuration
212 """
213 varname = params[0]
214 value = str(params[1])
215 setattr(command.cooker.configuration, varname, value)
216
217 def enableDataTracking(self, command, params):
218 """
219 Enable history tracking for variables
220 """
221 command.cooker.enableDataTracking()
222
223 def disableDataTracking(self, command, params):
224 """
225 Disable history tracking for variables
226 """
227 command.cooker.disableDataTracking()
228
229 def setPrePostConfFiles(self, command, params):
230 prefiles = params[0].split()
231 postfiles = params[1].split()
232 command.cooker.configuration.prefile = prefiles
233 command.cooker.configuration.postfile = postfiles
234 setPrePostConfFiles.needconfig = False
235
236 def getCpuCount(self, command, params):
237 """
238 Get the CPU count on the bitbake server
239 """
240 return bb.utils.cpu_count()
241 getCpuCount.readonly = True
242 getCpuCount.needconfig = False
243
244 def matchFile(self, command, params):
245 fMatch = params[0]
246 return command.cooker.matchFile(fMatch)
247 matchFile.needconfig = False
248
249 def generateNewImage(self, command, params):
250 image = params[0]
251 base_image = params[1]
252 package_queue = params[2]
253 timestamp = params[3]
254 description = params[4]
255 return command.cooker.generateNewImage(image, base_image,
256 package_queue, timestamp, description)
257
258 def ensureDir(self, command, params):
259 directory = params[0]
260 bb.utils.mkdirhier(directory)
261 ensureDir.needconfig = False
262
263 def setVarFile(self, command, params):
264 """
265 Save a variable in a file; used for saving in a configuration file
266 """
267 var = params[0]
268 val = params[1]
269 default_file = params[2]
270 op = params[3]
271 command.cooker.modifyConfigurationVar(var, val, default_file, op)
272 setVarFile.needconfig = False
273
274 def removeVarFile(self, command, params):
275 """
276 Remove a variable declaration from a file
277 """
278 var = params[0]
279 command.cooker.removeConfigurationVar(var)
280 removeVarFile.needconfig = False
281
282 def createConfigFile(self, command, params):
283 """
284 Create an extra configuration file
285 """
286 name = params[0]
287 command.cooker.createConfigFile(name)
288 createConfigFile.needconfig = False
289
290 def setEventMask(self, command, params):
291 handlerNum = params[0]
292 llevel = params[1]
293 debug_domains = params[2]
294 mask = params[3]
295 return bb.event.set_UIHmask(handlerNum, llevel, debug_domains, mask)
296 setEventMask.needconfig = False
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500297 setEventMask.readonly = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500298
299 def setFeatures(self, command, params):
300 """
301 Set the cooker features to include the passed list of features
302 """
303 features = params[0]
304 command.cooker.setFeatures(features)
305 setFeatures.needconfig = False
306 # although we change the internal state of the cooker, this is transparent since
307 # we always take and leave the cooker in state.initial
308 setFeatures.readonly = True
309
310 def updateConfig(self, command, params):
311 options = params[0]
312 environment = params[1]
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500313 cmdline = params[2]
314 command.cooker.updateConfigOpts(options, environment, cmdline)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500315 updateConfig.needconfig = False
316
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500317 def parseConfiguration(self, command, params):
318 """Instruct bitbake to parse its configuration
319 NOTE: it is only necessary to call this if you aren't calling any normal action
320 (otherwise parsing is taken care of automatically)
321 """
322 command.cooker.parseConfiguration()
323 parseConfiguration.needconfig = False
324
325 def getLayerPriorities(self, command, params):
326 ret = []
327 # regex objects cannot be marshalled by xmlrpc
328 for collection, pattern, regex, pri in command.cooker.bbfile_config_priorities:
329 ret.append((collection, pattern, regex.pattern, pri))
330 return ret
331 getLayerPriorities.readonly = True
332
333 def getRecipes(self, command, params):
334 try:
335 mc = params[0]
336 except IndexError:
337 mc = ''
338 return list(command.cooker.recipecaches[mc].pkg_pn.items())
339 getRecipes.readonly = True
340
341 def getRecipeDepends(self, command, params):
342 try:
343 mc = params[0]
344 except IndexError:
345 mc = ''
346 return list(command.cooker.recipecaches[mc].deps.items())
347 getRecipeDepends.readonly = True
348
349 def getRecipeVersions(self, command, params):
350 try:
351 mc = params[0]
352 except IndexError:
353 mc = ''
354 return command.cooker.recipecaches[mc].pkg_pepvpr
355 getRecipeVersions.readonly = True
356
357 def getRuntimeDepends(self, command, params):
358 ret = []
359 try:
360 mc = params[0]
361 except IndexError:
362 mc = ''
363 rundeps = command.cooker.recipecaches[mc].rundeps
364 for key, value in rundeps.items():
365 if isinstance(value, defaultdict):
366 value = dict(value)
367 ret.append((key, value))
368 return ret
369 getRuntimeDepends.readonly = True
370
371 def getRuntimeRecommends(self, command, params):
372 ret = []
373 try:
374 mc = params[0]
375 except IndexError:
376 mc = ''
377 runrecs = command.cooker.recipecaches[mc].runrecs
378 for key, value in runrecs.items():
379 if isinstance(value, defaultdict):
380 value = dict(value)
381 ret.append((key, value))
382 return ret
383 getRuntimeRecommends.readonly = True
384
385 def getRecipeInherits(self, command, params):
386 try:
387 mc = params[0]
388 except IndexError:
389 mc = ''
390 return command.cooker.recipecaches[mc].inherits
391 getRecipeInherits.readonly = True
392
393 def getBbFilePriority(self, command, params):
394 try:
395 mc = params[0]
396 except IndexError:
397 mc = ''
398 return command.cooker.recipecaches[mc].bbfile_priority
399 getBbFilePriority.readonly = True
400
401 def getDefaultPreference(self, command, params):
402 try:
403 mc = params[0]
404 except IndexError:
405 mc = ''
406 return command.cooker.recipecaches[mc].pkg_dp
407 getDefaultPreference.readonly = True
408
409 def getSkippedRecipes(self, command, params):
410 # Return list sorted by reverse priority order
411 import bb.cache
412 skipdict = OrderedDict(sorted(command.cooker.skiplist.items(),
413 key=lambda x: (-command.cooker.collection.calc_bbfile_priority(bb.cache.virtualfn2realfn(x[0])[0]), x[0])))
414 return list(skipdict.items())
415 getSkippedRecipes.readonly = True
416
417 def getOverlayedRecipes(self, command, params):
418 return list(command.cooker.collection.overlayed.items())
419 getOverlayedRecipes.readonly = True
420
421 def getFileAppends(self, command, params):
422 fn = params[0]
423 return command.cooker.collection.get_file_appends(fn)
424 getFileAppends.readonly = True
425
426 def getAllAppends(self, command, params):
427 return command.cooker.collection.bbappends
428 getAllAppends.readonly = True
429
430 def findProviders(self, command, params):
431 return command.cooker.findProviders()
432 findProviders.readonly = True
433
434 def findBestProvider(self, command, params):
435 (mc, pn) = split_mc_pn(params[0])
436 return command.cooker.findBestProvider(pn, mc)
437 findBestProvider.readonly = True
438
439 def allProviders(self, command, params):
440 try:
441 mc = params[0]
442 except IndexError:
443 mc = ''
444 return list(bb.providers.allProviders(command.cooker.recipecaches[mc]).items())
445 allProviders.readonly = True
446
447 def getRuntimeProviders(self, command, params):
448 rprovide = params[0]
449 try:
450 mc = params[1]
451 except IndexError:
452 mc = ''
453 all_p = bb.providers.getRuntimeProviders(command.cooker.recipecaches[mc], rprovide)
454 if all_p:
455 best = bb.providers.filterProvidersRunTime(all_p, rprovide,
456 command.cooker.data,
457 command.cooker.recipecaches[mc])[0][0]
458 else:
459 best = None
460 return all_p, best
461 getRuntimeProviders.readonly = True
462
463 def dataStoreConnectorFindVar(self, command, params):
464 dsindex = params[0]
465 name = params[1]
466 datastore = command.remotedatastores[dsindex]
467 value, overridedata = datastore._findVar(name)
468
469 if value:
470 content = value.get('_content', None)
471 if isinstance(content, bb.data_smart.DataSmart):
472 # Value is a datastore (e.g. BB_ORIGENV) - need to handle this carefully
473 idx = command.remotedatastores.check_store(content, True)
474 return {'_content': DataStoreConnectionHandle(idx),
475 '_connector_origtype': 'DataStoreConnectionHandle',
476 '_connector_overrides': overridedata}
477 elif isinstance(content, set):
478 return {'_content': list(content),
479 '_connector_origtype': 'set',
480 '_connector_overrides': overridedata}
481 else:
482 value['_connector_overrides'] = overridedata
483 else:
484 value = {}
485 value['_connector_overrides'] = overridedata
486 return value
487 dataStoreConnectorFindVar.readonly = True
488
489 def dataStoreConnectorGetKeys(self, command, params):
490 dsindex = params[0]
491 datastore = command.remotedatastores[dsindex]
492 return list(datastore.keys())
493 dataStoreConnectorGetKeys.readonly = True
494
495 def dataStoreConnectorGetVarHistory(self, command, params):
496 dsindex = params[0]
497 name = params[1]
498 datastore = command.remotedatastores[dsindex]
499 return datastore.varhistory.variable(name)
500 dataStoreConnectorGetVarHistory.readonly = True
501
502 def dataStoreConnectorExpandPythonRef(self, command, params):
503 config_data_dict = params[0]
504 varname = params[1]
505 expr = params[2]
506
507 config_data = command.remotedatastores.receive_datastore(config_data_dict)
508
509 varparse = bb.data_smart.VariableParse(varname, config_data)
510 return varparse.python_sub(expr)
511
512 def dataStoreConnectorRelease(self, command, params):
513 dsindex = params[0]
514 if dsindex <= 0:
515 raise CommandError('dataStoreConnectorRelease: invalid index %d' % dsindex)
516 command.remotedatastores.release(dsindex)
517
518 def dataStoreConnectorSetVarFlag(self, command, params):
519 dsindex = params[0]
520 name = params[1]
521 flag = params[2]
522 value = params[3]
523 datastore = command.remotedatastores[dsindex]
524 datastore.setVarFlag(name, flag, value)
525
526 def dataStoreConnectorDelVar(self, command, params):
527 dsindex = params[0]
528 name = params[1]
529 datastore = command.remotedatastores[dsindex]
530 if len(params) > 2:
531 flag = params[2]
532 datastore.delVarFlag(name, flag)
533 else:
534 datastore.delVar(name)
535
536 def dataStoreConnectorRenameVar(self, command, params):
537 dsindex = params[0]
538 name = params[1]
539 newname = params[2]
540 datastore = command.remotedatastores[dsindex]
541 datastore.renameVar(name, newname)
542
543 def parseRecipeFile(self, command, params):
544 """
545 Parse the specified recipe file (with or without bbappends)
546 and return a datastore object representing the environment
547 for the recipe.
548 """
549 fn = params[0]
550 appends = params[1]
551 appendlist = params[2]
552 if len(params) > 3:
553 config_data_dict = params[3]
554 config_data = command.remotedatastores.receive_datastore(config_data_dict)
555 else:
556 config_data = None
557
558 if appends:
559 if appendlist is not None:
560 appendfiles = appendlist
561 else:
562 appendfiles = command.cooker.collection.get_file_appends(fn)
563 else:
564 appendfiles = []
565 # We are calling bb.cache locally here rather than on the server,
566 # but that's OK because it doesn't actually need anything from
567 # the server barring the global datastore (which we have a remote
568 # version of)
569 if config_data:
570 # We have to use a different function here if we're passing in a datastore
571 # NOTE: we took a copy above, so we don't do it here again
572 envdata = bb.cache.parse_recipe(config_data, fn, appendfiles)['']
573 else:
574 # Use the standard path
575 parser = bb.cache.NoCache(command.cooker.databuilder)
576 envdata = parser.loadDataFull(fn, appendfiles)
577 idx = command.remotedatastores.store(envdata)
578 return DataStoreConnectionHandle(idx)
579 parseRecipeFile.readonly = True
580
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500581class CommandsAsync:
582 """
583 A class of asynchronous commands
584 These functions communicate via generated events.
585 Any function that requires metadata parsing should be here.
586 """
587
588 def buildFile(self, command, params):
589 """
590 Build a single specified .bb file
591 """
592 bfile = params[0]
593 task = params[1]
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500594 if len(params) > 2:
595 hidewarning = params[2]
596 else:
597 hidewarning = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500598
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500599 command.cooker.buildFile(bfile, task, hidewarning)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500600 buildFile.needcache = False
601
602 def buildTargets(self, command, params):
603 """
604 Build a set of targets
605 """
606 pkgs_to_build = params[0]
607 task = params[1]
608
609 command.cooker.buildTargets(pkgs_to_build, task)
610 buildTargets.needcache = True
611
612 def generateDepTreeEvent(self, command, params):
613 """
614 Generate an event containing the dependency information
615 """
616 pkgs_to_build = params[0]
617 task = params[1]
618
619 command.cooker.generateDepTreeEvent(pkgs_to_build, task)
620 command.finishAsyncCommand()
621 generateDepTreeEvent.needcache = True
622
623 def generateDotGraph(self, command, params):
624 """
625 Dump dependency information to disk as .dot files
626 """
627 pkgs_to_build = params[0]
628 task = params[1]
629
630 command.cooker.generateDotGraphFiles(pkgs_to_build, task)
631 command.finishAsyncCommand()
632 generateDotGraph.needcache = True
633
634 def generateTargetsTree(self, command, params):
635 """
636 Generate a tree of buildable targets.
637 If klass is provided ensure all recipes that inherit the class are
638 included in the package list.
639 If pkg_list provided use that list (plus any extras brought in by
640 klass) rather than generating a tree for all packages.
641 """
642 klass = params[0]
643 pkg_list = params[1]
644
645 command.cooker.generateTargetsTree(klass, pkg_list)
646 command.finishAsyncCommand()
647 generateTargetsTree.needcache = True
648
649 def findCoreBaseFiles(self, command, params):
650 """
651 Find certain files in COREBASE directory. i.e. Layers
652 """
653 subdir = params[0]
654 filename = params[1]
655
656 command.cooker.findCoreBaseFiles(subdir, filename)
657 command.finishAsyncCommand()
658 findCoreBaseFiles.needcache = False
659
660 def findConfigFiles(self, command, params):
661 """
662 Find config files which provide appropriate values
663 for the passed configuration variable. i.e. MACHINE
664 """
665 varname = params[0]
666
667 command.cooker.findConfigFiles(varname)
668 command.finishAsyncCommand()
669 findConfigFiles.needcache = False
670
671 def findFilesMatchingInDir(self, command, params):
672 """
673 Find implementation files matching the specified pattern
674 in the requested subdirectory of a BBPATH
675 """
676 pattern = params[0]
677 directory = params[1]
678
679 command.cooker.findFilesMatchingInDir(pattern, directory)
680 command.finishAsyncCommand()
681 findFilesMatchingInDir.needcache = False
682
683 def findConfigFilePath(self, command, params):
684 """
685 Find the path of the requested configuration file
686 """
687 configfile = params[0]
688
689 command.cooker.findConfigFilePath(configfile)
690 command.finishAsyncCommand()
691 findConfigFilePath.needcache = False
692
693 def showVersions(self, command, params):
694 """
695 Show the currently selected versions
696 """
697 command.cooker.showVersions()
698 command.finishAsyncCommand()
699 showVersions.needcache = True
700
701 def showEnvironmentTarget(self, command, params):
702 """
703 Print the environment of a target recipe
704 (needs the cache to work out which recipe to use)
705 """
706 pkg = params[0]
707
708 command.cooker.showEnvironment(None, pkg)
709 command.finishAsyncCommand()
710 showEnvironmentTarget.needcache = True
711
712 def showEnvironment(self, command, params):
713 """
714 Print the standard environment
715 or if specified the environment for a specified recipe
716 """
717 bfile = params[0]
718
719 command.cooker.showEnvironment(bfile)
720 command.finishAsyncCommand()
721 showEnvironment.needcache = False
722
723 def parseFiles(self, command, params):
724 """
725 Parse the .bb files
726 """
727 command.cooker.updateCache()
728 command.finishAsyncCommand()
729 parseFiles.needcache = True
730
731 def compareRevisions(self, command, params):
732 """
733 Parse the .bb files
734 """
735 if bb.fetch.fetcher_compare_revisions(command.cooker.data):
736 command.finishAsyncCommand(code=1)
737 else:
738 command.finishAsyncCommand()
739 compareRevisions.needcache = True
740
741 def triggerEvent(self, command, params):
742 """
743 Trigger a certain event
744 """
745 event = params[0]
746 bb.event.fire(eval(event), command.cooker.data)
747 command.currentAsyncCommand = None
748 triggerEvent.needcache = False
749
750 def resetCooker(self, command, params):
751 """
752 Reset the cooker to its initial state, thus forcing a reparse for
753 any async command that has the needcache property set to True
754 """
755 command.cooker.reset()
756 command.finishAsyncCommand()
757 resetCooker.needcache = False
758
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500759 def clientComplete(self, command, params):
760 """
761 Do the right thing when the controlling client exits
762 """
763 command.cooker.clientComplete()
764 command.finishAsyncCommand()
765 clientComplete.needcache = False
766