blob: 1fcb9bf14ced8d391006b97ff78fad6bf08578a1 [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#
Brad Bishopc342db32019-05-15 21:57:59 -04009# SPDX-License-Identifier: GPL-2.0-only
Patrick Williamsc124f4f2015-09-15 14:41:29 -050010#
Patrick Williamsc124f4f2015-09-15 14:41:29 -050011
12"""
13The bitbake server takes 'commands' from its UI/commandline.
14Commands are either synchronous or asynchronous.
15Async commands return data to the client in the form of events.
16Sync commands must only return data through the function return value
17and must not trigger events, directly or indirectly.
18Commands are queued in a CommandQueue
19"""
20
Brad Bishop6e60e8b2018-02-01 10:27:11 -050021from collections import OrderedDict, defaultdict
22
Andrew Geisslerc926e172021-05-07 16:11:35 -050023import io
Patrick Williamsc124f4f2015-09-15 14:41:29 -050024import bb.event
25import bb.cooker
Brad Bishop6e60e8b2018-02-01 10:27:11 -050026import bb.remotedata
27
28class DataStoreConnectionHandle(object):
29 def __init__(self, dsindex=0):
30 self.dsindex = dsindex
Patrick Williamsc124f4f2015-09-15 14:41:29 -050031
32class CommandCompleted(bb.event.Event):
33 pass
34
35class CommandExit(bb.event.Event):
36 def __init__(self, exitcode):
37 bb.event.Event.__init__(self)
38 self.exitcode = int(exitcode)
39
40class CommandFailed(CommandExit):
41 def __init__(self, message):
42 self.error = message
43 CommandExit.__init__(self, 1)
Brad Bishopd7bf8c12018-02-25 22:55:05 -050044 def __str__(self):
45 return "Command execution failed: %s" % self.error
Patrick Williamsc124f4f2015-09-15 14:41:29 -050046
47class CommandError(Exception):
48 pass
49
50class Command:
51 """
52 A queue of asynchronous commands for bitbake
53 """
Andrew Geissler517393d2023-01-13 08:55:19 -060054 def __init__(self, cooker, process_server):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050055 self.cooker = cooker
56 self.cmds_sync = CommandsSync()
57 self.cmds_async = CommandsAsync()
Andrew Geisslerc9f78652020-09-18 14:11:35 -050058 self.remotedatastores = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -050059
Andrew Geissler517393d2023-01-13 08:55:19 -060060 self.process_server = process_server
61 # Access with locking using process_server.{get/set/clear}_async_cmd()
Patrick Williamsc124f4f2015-09-15 14:41:29 -050062 self.currentAsyncCommand = None
63
Andrew Geissler517393d2023-01-13 08:55:19 -060064 def runCommand(self, commandline, process_server, ro_only=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050065 command = commandline.pop(0)
Andrew Geisslerc9f78652020-09-18 14:11:35 -050066
67 # Ensure cooker is ready for commands
Andrew Geissler5082cc72023-09-11 08:41:39 -040068 if command not in ["updateConfig", "setFeatures", "ping"]:
Patrick Williams213cb262021-08-07 19:21:33 -050069 try:
70 self.cooker.init_configdata()
71 if not self.remotedatastores:
72 self.remotedatastores = bb.remotedata.RemoteDatastores(self.cooker)
73 except (Exception, SystemExit) as exc:
74 import traceback
75 if isinstance(exc, bb.BBHandledException):
76 # We need to start returning real exceptions here. Until we do, we can't
77 # tell if an exception is an instance of bb.BBHandledException
78 return None, "bb.BBHandledException()\n" + traceback.format_exc()
79 return None, traceback.format_exc()
Andrew Geisslerc9f78652020-09-18 14:11:35 -050080
Patrick Williamsc124f4f2015-09-15 14:41:29 -050081 if hasattr(CommandsSync, command):
82 # Can run synchronous commands straight away
83 command_method = getattr(self.cmds_sync, command)
84 if ro_only:
Andrew Geissler82c905d2020-04-13 13:39:40 -050085 if not hasattr(command_method, 'readonly') or not getattr(command_method, 'readonly'):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050086 return None, "Not able to execute not readonly commands in readonly mode"
87 try:
Brad Bishopd7bf8c12018-02-25 22:55:05 -050088 if getattr(command_method, 'needconfig', True):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050089 self.cooker.updateCacheSync()
90 result = command_method(self, commandline)
91 except CommandError as exc:
92 return None, exc.args[0]
Andrew Geisslerf0343792020-11-18 10:42:21 -060093 except (Exception, SystemExit) as exc:
Patrick Williamsc124f4f2015-09-15 14:41:29 -050094 import traceback
Andrew Geisslerf0343792020-11-18 10:42:21 -060095 if isinstance(exc, bb.BBHandledException):
96 # We need to start returning real exceptions here. Until we do, we can't
97 # tell if an exception is an instance of bb.BBHandledException
98 return None, "bb.BBHandledException()\n" + traceback.format_exc()
Patrick Williamsc124f4f2015-09-15 14:41:29 -050099 return None, traceback.format_exc()
100 else:
101 return result, None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500102 if command not in CommandsAsync.__dict__:
103 return None, "No such command"
Andrew Geissler517393d2023-01-13 08:55:19 -0600104 if not process_server.set_async_cmd((command, commandline)):
105 return None, "Busy (%s in progress)" % self.process_server.get_async_cmd()[0]
106 self.cooker.idleCallBackRegister(self.runAsyncCommand, process_server)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500107 return True, None
108
Andrew Geissler517393d2023-01-13 08:55:19 -0600109 def runAsyncCommand(self, _, process_server, halt):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500110 try:
111 if self.cooker.state in (bb.cooker.state.error, bb.cooker.state.shutdown, bb.cooker.state.forceshutdown):
112 # updateCache will trigger a shutdown of the parser
113 # and then raise BBHandledException triggering an exit
114 self.cooker.updateCache()
Andrew Geissler517393d2023-01-13 08:55:19 -0600115 return bb.server.process.idleFinish("Cooker in error state")
116 cmd = process_server.get_async_cmd()
117 if cmd is not None:
118 (command, options) = cmd
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500119 commandmethod = getattr(CommandsAsync, command)
120 needcache = getattr( commandmethod, "needcache" )
121 if needcache and self.cooker.state != bb.cooker.state.running:
122 self.cooker.updateCache()
123 return True
124 else:
125 commandmethod(self.cmds_async, self, options)
126 return False
127 else:
Andrew Geissler517393d2023-01-13 08:55:19 -0600128 return bb.server.process.idleFinish("Nothing to do, no async command?")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500129 except KeyboardInterrupt as exc:
Andrew Geissler517393d2023-01-13 08:55:19 -0600130 return bb.server.process.idleFinish("Interrupted")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500131 except SystemExit as exc:
132 arg = exc.args[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600133 if isinstance(arg, str):
Andrew Geissler517393d2023-01-13 08:55:19 -0600134 return bb.server.process.idleFinish(arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500135 else:
Andrew Geissler517393d2023-01-13 08:55:19 -0600136 return bb.server.process.idleFinish("Exited with %s" % arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500137 except Exception as exc:
138 import traceback
139 if isinstance(exc, bb.BBHandledException):
Andrew Geissler517393d2023-01-13 08:55:19 -0600140 return bb.server.process.idleFinish("")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500141 else:
Andrew Geissler517393d2023-01-13 08:55:19 -0600142 return bb.server.process.idleFinish(traceback.format_exc())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500143
144 def finishAsyncCommand(self, msg=None, code=None):
145 if msg or msg == "":
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500146 bb.event.fire(CommandFailed(msg), self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500147 elif code:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500148 bb.event.fire(CommandExit(code), self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500149 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500150 bb.event.fire(CommandCompleted(), self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500151 self.cooker.finishcommand()
Andrew Geissler517393d2023-01-13 08:55:19 -0600152 self.process_server.clear_async_cmd()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500153
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500154 def reset(self):
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500155 if self.remotedatastores:
156 self.remotedatastores = bb.remotedata.RemoteDatastores(self.cooker)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500157
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500158class CommandsSync:
159 """
160 A class of synchronous commands
161 These should run quickly so as not to hurt interactive performance.
162 These must not influence any running synchronous command.
163 """
164
Andrew Geissler517393d2023-01-13 08:55:19 -0600165 def ping(self, command, params):
166 """
167 Allow a UI to check the server is still alive
168 """
169 return "Still alive!"
Andrew Geissler5082cc72023-09-11 08:41:39 -0400170 ping.needconfig = False
171 ping.readonly = True
Andrew Geissler517393d2023-01-13 08:55:19 -0600172
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500173 def stateShutdown(self, command, params):
174 """
175 Trigger cooker 'shutdown' mode
176 """
177 command.cooker.shutdown(False)
178
179 def stateForceShutdown(self, command, params):
180 """
181 Stop the cooker
182 """
183 command.cooker.shutdown(True)
184
185 def getAllKeysWithFlags(self, command, params):
186 """
187 Returns a dump of the global state. Call with
188 variable flags to be retrieved as params.
189 """
190 flaglist = params[0]
191 return command.cooker.getAllKeysWithFlags(flaglist)
192 getAllKeysWithFlags.readonly = True
193
194 def getVariable(self, command, params):
195 """
196 Read the value of a variable from data
197 """
198 varname = params[0]
199 expand = True
200 if len(params) > 1:
201 expand = (params[1] == "True")
202
203 return command.cooker.data.getVar(varname, expand)
204 getVariable.readonly = True
205
206 def setVariable(self, command, params):
207 """
208 Set the value of variable in data
209 """
210 varname = params[0]
211 value = str(params[1])
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500212 command.cooker.extraconfigdata[varname] = value
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500213 command.cooker.data.setVar(varname, value)
214
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500215 def getSetVariable(self, command, params):
216 """
217 Read the value of a variable from data and set it into the datastore
218 which effectively expands and locks the value.
219 """
220 varname = params[0]
221 result = self.getVariable(command, params)
222 command.cooker.data.setVar(varname, result)
223 return result
224
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500225 def setConfig(self, command, params):
226 """
227 Set the value of variable in configuration
228 """
229 varname = params[0]
230 value = str(params[1])
231 setattr(command.cooker.configuration, varname, value)
232
233 def enableDataTracking(self, command, params):
234 """
235 Enable history tracking for variables
236 """
237 command.cooker.enableDataTracking()
238
239 def disableDataTracking(self, command, params):
240 """
241 Disable history tracking for variables
242 """
243 command.cooker.disableDataTracking()
244
245 def setPrePostConfFiles(self, command, params):
246 prefiles = params[0].split()
247 postfiles = params[1].split()
248 command.cooker.configuration.prefile = prefiles
249 command.cooker.configuration.postfile = postfiles
250 setPrePostConfFiles.needconfig = False
251
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500252 def matchFile(self, command, params):
253 fMatch = params[0]
Andrew Geissler5a43b432020-06-13 10:46:56 -0500254 try:
255 mc = params[0]
256 except IndexError:
257 mc = ''
258 return command.cooker.matchFile(fMatch, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500259 matchFile.needconfig = False
260
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500261 def getUIHandlerNum(self, command, params):
262 return bb.event.get_uihandler()
263 getUIHandlerNum.needconfig = False
264 getUIHandlerNum.readonly = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500265
266 def setEventMask(self, command, params):
267 handlerNum = params[0]
268 llevel = params[1]
269 debug_domains = params[2]
270 mask = params[3]
271 return bb.event.set_UIHmask(handlerNum, llevel, debug_domains, mask)
272 setEventMask.needconfig = False
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500273 setEventMask.readonly = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500274
275 def setFeatures(self, command, params):
276 """
277 Set the cooker features to include the passed list of features
278 """
279 features = params[0]
280 command.cooker.setFeatures(features)
281 setFeatures.needconfig = False
282 # although we change the internal state of the cooker, this is transparent since
283 # we always take and leave the cooker in state.initial
284 setFeatures.readonly = True
285
286 def updateConfig(self, command, params):
287 options = params[0]
288 environment = params[1]
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500289 cmdline = params[2]
290 command.cooker.updateConfigOpts(options, environment, cmdline)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500291 updateConfig.needconfig = False
292
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500293 def parseConfiguration(self, command, params):
294 """Instruct bitbake to parse its configuration
295 NOTE: it is only necessary to call this if you aren't calling any normal action
296 (otherwise parsing is taken care of automatically)
297 """
298 command.cooker.parseConfiguration()
299 parseConfiguration.needconfig = False
300
301 def getLayerPriorities(self, command, params):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500302 command.cooker.parseConfiguration()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500303 ret = []
304 # regex objects cannot be marshalled by xmlrpc
305 for collection, pattern, regex, pri in command.cooker.bbfile_config_priorities:
306 ret.append((collection, pattern, regex.pattern, pri))
307 return ret
308 getLayerPriorities.readonly = True
309
Andrew Geissler220dafd2023-10-04 10:18:08 -0500310 def revalidateCaches(self, command, params):
311 """Called by UI clients when metadata may have changed"""
312 command.cooker.revalidateCaches()
313 parseConfiguration.needconfig = False
314
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500315 def getRecipes(self, command, params):
316 try:
317 mc = params[0]
318 except IndexError:
319 mc = ''
320 return list(command.cooker.recipecaches[mc].pkg_pn.items())
321 getRecipes.readonly = True
322
323 def getRecipeDepends(self, command, params):
324 try:
325 mc = params[0]
326 except IndexError:
327 mc = ''
328 return list(command.cooker.recipecaches[mc].deps.items())
329 getRecipeDepends.readonly = True
330
331 def getRecipeVersions(self, command, params):
332 try:
333 mc = params[0]
334 except IndexError:
335 mc = ''
336 return command.cooker.recipecaches[mc].pkg_pepvpr
337 getRecipeVersions.readonly = True
338
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500339 def getRecipeProvides(self, command, params):
340 try:
341 mc = params[0]
342 except IndexError:
343 mc = ''
344 return command.cooker.recipecaches[mc].fn_provides
345 getRecipeProvides.readonly = True
346
347 def getRecipePackages(self, command, params):
348 try:
349 mc = params[0]
350 except IndexError:
351 mc = ''
352 return command.cooker.recipecaches[mc].packages
353 getRecipePackages.readonly = True
354
355 def getRecipePackagesDynamic(self, command, params):
356 try:
357 mc = params[0]
358 except IndexError:
359 mc = ''
360 return command.cooker.recipecaches[mc].packages_dynamic
361 getRecipePackagesDynamic.readonly = True
362
363 def getRProviders(self, command, params):
364 try:
365 mc = params[0]
366 except IndexError:
367 mc = ''
368 return command.cooker.recipecaches[mc].rproviders
369 getRProviders.readonly = True
370
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500371 def getRuntimeDepends(self, command, params):
372 ret = []
373 try:
374 mc = params[0]
375 except IndexError:
376 mc = ''
377 rundeps = command.cooker.recipecaches[mc].rundeps
378 for key, value in rundeps.items():
379 if isinstance(value, defaultdict):
380 value = dict(value)
381 ret.append((key, value))
382 return ret
383 getRuntimeDepends.readonly = True
384
385 def getRuntimeRecommends(self, command, params):
386 ret = []
387 try:
388 mc = params[0]
389 except IndexError:
390 mc = ''
391 runrecs = command.cooker.recipecaches[mc].runrecs
392 for key, value in runrecs.items():
393 if isinstance(value, defaultdict):
394 value = dict(value)
395 ret.append((key, value))
396 return ret
397 getRuntimeRecommends.readonly = True
398
399 def getRecipeInherits(self, command, params):
400 try:
401 mc = params[0]
402 except IndexError:
403 mc = ''
404 return command.cooker.recipecaches[mc].inherits
405 getRecipeInherits.readonly = True
406
407 def getBbFilePriority(self, command, params):
408 try:
409 mc = params[0]
410 except IndexError:
411 mc = ''
412 return command.cooker.recipecaches[mc].bbfile_priority
413 getBbFilePriority.readonly = True
414
415 def getDefaultPreference(self, command, params):
416 try:
417 mc = params[0]
418 except IndexError:
419 mc = ''
420 return command.cooker.recipecaches[mc].pkg_dp
421 getDefaultPreference.readonly = True
422
423 def getSkippedRecipes(self, command, params):
424 # Return list sorted by reverse priority order
425 import bb.cache
Andrew Geissler5a43b432020-06-13 10:46:56 -0500426 def sortkey(x):
427 vfn, _ = x
428 realfn, _, mc = bb.cache.virtualfn2realfn(vfn)
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500429 return (-command.cooker.collections[mc].calc_bbfile_priority(realfn)[0], vfn)
Andrew Geissler5a43b432020-06-13 10:46:56 -0500430
431 skipdict = OrderedDict(sorted(command.cooker.skiplist.items(), key=sortkey))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500432 return list(skipdict.items())
433 getSkippedRecipes.readonly = True
434
435 def getOverlayedRecipes(self, command, params):
Andrew Geissler5a43b432020-06-13 10:46:56 -0500436 try:
437 mc = params[0]
438 except IndexError:
439 mc = ''
440 return list(command.cooker.collections[mc].overlayed.items())
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500441 getOverlayedRecipes.readonly = True
442
443 def getFileAppends(self, command, params):
444 fn = params[0]
Andrew Geissler5a43b432020-06-13 10:46:56 -0500445 try:
446 mc = params[1]
447 except IndexError:
448 mc = ''
449 return command.cooker.collections[mc].get_file_appends(fn)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500450 getFileAppends.readonly = True
451
452 def getAllAppends(self, command, params):
Andrew Geissler5a43b432020-06-13 10:46:56 -0500453 try:
454 mc = params[0]
455 except IndexError:
456 mc = ''
457 return command.cooker.collections[mc].bbappends
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500458 getAllAppends.readonly = True
459
460 def findProviders(self, command, params):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500461 try:
462 mc = params[0]
463 except IndexError:
464 mc = ''
465 return command.cooker.findProviders(mc)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500466 findProviders.readonly = True
467
468 def findBestProvider(self, command, params):
Andrew Geissler5a43b432020-06-13 10:46:56 -0500469 (mc, pn) = bb.runqueue.split_mc(params[0])
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500470 return command.cooker.findBestProvider(pn, mc)
471 findBestProvider.readonly = True
472
473 def allProviders(self, command, params):
474 try:
475 mc = params[0]
476 except IndexError:
477 mc = ''
478 return list(bb.providers.allProviders(command.cooker.recipecaches[mc]).items())
479 allProviders.readonly = True
480
481 def getRuntimeProviders(self, command, params):
482 rprovide = params[0]
483 try:
484 mc = params[1]
485 except IndexError:
486 mc = ''
487 all_p = bb.providers.getRuntimeProviders(command.cooker.recipecaches[mc], rprovide)
488 if all_p:
489 best = bb.providers.filterProvidersRunTime(all_p, rprovide,
490 command.cooker.data,
491 command.cooker.recipecaches[mc])[0][0]
492 else:
493 best = None
494 return all_p, best
495 getRuntimeProviders.readonly = True
496
Andrew Geissler82c905d2020-04-13 13:39:40 -0500497 def dataStoreConnectorCmd(self, command, params):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500498 dsindex = params[0]
Andrew Geissler82c905d2020-04-13 13:39:40 -0500499 method = params[1]
500 args = params[2]
501 kwargs = params[3]
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500502
Andrew Geissler82c905d2020-04-13 13:39:40 -0500503 d = command.remotedatastores[dsindex]
504 ret = getattr(d, method)(*args, **kwargs)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500505
Andrew Geissler82c905d2020-04-13 13:39:40 -0500506 if isinstance(ret, bb.data_smart.DataSmart):
507 idx = command.remotedatastores.store(ret)
508 return DataStoreConnectionHandle(idx)
509
510 return ret
511
512 def dataStoreConnectorVarHistCmd(self, command, params):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500513 dsindex = params[0]
Andrew Geissler82c905d2020-04-13 13:39:40 -0500514 method = params[1]
515 args = params[2]
516 kwargs = params[3]
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500517
Andrew Geissler82c905d2020-04-13 13:39:40 -0500518 d = command.remotedatastores[dsindex].varhistory
519 return getattr(d, method)(*args, **kwargs)
520
Andrew Geisslerc926e172021-05-07 16:11:35 -0500521 def dataStoreConnectorVarHistCmdEmit(self, command, params):
522 dsindex = params[0]
523 var = params[1]
524 oval = params[2]
525 val = params[3]
526 d = command.remotedatastores[params[4]]
527
528 o = io.StringIO()
529 command.remotedatastores[dsindex].varhistory.emit(var, oval, val, o, d)
530 return o.getvalue()
531
Andrew Geissler82c905d2020-04-13 13:39:40 -0500532 def dataStoreConnectorIncHistCmd(self, command, params):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500533 dsindex = params[0]
Andrew Geissler82c905d2020-04-13 13:39:40 -0500534 method = params[1]
535 args = params[2]
536 kwargs = params[3]
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500537
Andrew Geissler82c905d2020-04-13 13:39:40 -0500538 d = command.remotedatastores[dsindex].inchistory
539 return getattr(d, method)(*args, **kwargs)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500540
541 def dataStoreConnectorRelease(self, command, params):
542 dsindex = params[0]
543 if dsindex <= 0:
544 raise CommandError('dataStoreConnectorRelease: invalid index %d' % dsindex)
545 command.remotedatastores.release(dsindex)
546
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500547 def parseRecipeFile(self, command, params):
548 """
549 Parse the specified recipe file (with or without bbappends)
550 and return a datastore object representing the environment
551 for the recipe.
552 """
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600553 virtualfn = params[0]
554 (fn, cls, mc) = bb.cache.virtualfn2realfn(virtualfn)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500555 appends = params[1]
556 appendlist = params[2]
557 if len(params) > 3:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500558 config_data = command.remotedatastores[params[3]]
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500559 else:
560 config_data = None
561
562 if appends:
563 if appendlist is not None:
564 appendfiles = appendlist
565 else:
Andrew Geissler5a43b432020-06-13 10:46:56 -0500566 appendfiles = command.cooker.collections[mc].get_file_appends(fn)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500567 else:
568 appendfiles = []
Patrick Williamse760df82023-05-26 11:10:49 -0500569 layername = command.cooker.collections[mc].calc_bbfile_priority(fn)[2]
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500570 # We are calling bb.cache locally here rather than on the server,
571 # but that's OK because it doesn't actually need anything from
572 # the server barring the global datastore (which we have a remote
573 # version of)
574 if config_data:
575 # We have to use a different function here if we're passing in a datastore
576 # NOTE: we took a copy above, so we don't do it here again
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600577 envdata = command.cooker.databuilder._parse_recipe(config_data, fn, appendfiles, mc, layername)[cls]
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500578 else:
579 # Use the standard path
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600580 envdata = command.cooker.databuilder.parseRecipe(virtualfn, appendfiles, layername)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500581 idx = command.remotedatastores.store(envdata)
582 return DataStoreConnectionHandle(idx)
583 parseRecipeFile.readonly = True
584
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500585class CommandsAsync:
586 """
587 A class of asynchronous commands
588 These functions communicate via generated events.
589 Any function that requires metadata parsing should be here.
590 """
591
592 def buildFile(self, command, params):
593 """
594 Build a single specified .bb file
595 """
596 bfile = params[0]
597 task = params[1]
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500598 if len(params) > 2:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500599 internal = params[2]
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500600 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500601 internal = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500602
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500603 if internal:
604 command.cooker.buildFileInternal(bfile, task, fireevents=False, quietlog=True)
605 else:
606 command.cooker.buildFile(bfile, task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500607 buildFile.needcache = False
608
609 def buildTargets(self, command, params):
610 """
611 Build a set of targets
612 """
613 pkgs_to_build = params[0]
614 task = params[1]
615
616 command.cooker.buildTargets(pkgs_to_build, task)
617 buildTargets.needcache = True
618
619 def generateDepTreeEvent(self, command, params):
620 """
621 Generate an event containing the dependency information
622 """
623 pkgs_to_build = params[0]
624 task = params[1]
625
626 command.cooker.generateDepTreeEvent(pkgs_to_build, task)
627 command.finishAsyncCommand()
628 generateDepTreeEvent.needcache = True
629
630 def generateDotGraph(self, command, params):
631 """
632 Dump dependency information to disk as .dot files
633 """
634 pkgs_to_build = params[0]
635 task = params[1]
636
637 command.cooker.generateDotGraphFiles(pkgs_to_build, task)
638 command.finishAsyncCommand()
639 generateDotGraph.needcache = True
640
641 def generateTargetsTree(self, command, params):
642 """
643 Generate a tree of buildable targets.
644 If klass is provided ensure all recipes that inherit the class are
645 included in the package list.
646 If pkg_list provided use that list (plus any extras brought in by
647 klass) rather than generating a tree for all packages.
648 """
649 klass = params[0]
650 pkg_list = params[1]
651
652 command.cooker.generateTargetsTree(klass, pkg_list)
653 command.finishAsyncCommand()
654 generateTargetsTree.needcache = True
655
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500656 def findConfigFiles(self, command, params):
657 """
658 Find config files which provide appropriate values
659 for the passed configuration variable. i.e. MACHINE
660 """
661 varname = params[0]
662
663 command.cooker.findConfigFiles(varname)
664 command.finishAsyncCommand()
665 findConfigFiles.needcache = False
666
667 def findFilesMatchingInDir(self, command, params):
668 """
669 Find implementation files matching the specified pattern
670 in the requested subdirectory of a BBPATH
671 """
672 pattern = params[0]
673 directory = params[1]
674
675 command.cooker.findFilesMatchingInDir(pattern, directory)
676 command.finishAsyncCommand()
677 findFilesMatchingInDir.needcache = False
678
Patrick Williams93c203f2021-10-06 16:15:23 -0500679 def testCookerCommandEvent(self, command, params):
680 """
681 Dummy command used by OEQA selftest to test tinfoil without IO
682 """
683 pattern = params[0]
684
685 command.cooker.testCookerCommandEvent(pattern)
686 command.finishAsyncCommand()
687 testCookerCommandEvent.needcache = False
688
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500689 def findConfigFilePath(self, command, params):
690 """
691 Find the path of the requested configuration file
692 """
693 configfile = params[0]
694
695 command.cooker.findConfigFilePath(configfile)
696 command.finishAsyncCommand()
697 findConfigFilePath.needcache = False
698
699 def showVersions(self, command, params):
700 """
701 Show the currently selected versions
702 """
703 command.cooker.showVersions()
704 command.finishAsyncCommand()
705 showVersions.needcache = True
706
707 def showEnvironmentTarget(self, command, params):
708 """
709 Print the environment of a target recipe
710 (needs the cache to work out which recipe to use)
711 """
712 pkg = params[0]
713
714 command.cooker.showEnvironment(None, pkg)
715 command.finishAsyncCommand()
716 showEnvironmentTarget.needcache = True
717
718 def showEnvironment(self, command, params):
719 """
720 Print the standard environment
721 or if specified the environment for a specified recipe
722 """
723 bfile = params[0]
724
725 command.cooker.showEnvironment(bfile)
726 command.finishAsyncCommand()
727 showEnvironment.needcache = False
728
729 def parseFiles(self, command, params):
730 """
731 Parse the .bb files
732 """
733 command.cooker.updateCache()
734 command.finishAsyncCommand()
735 parseFiles.needcache = True
736
737 def compareRevisions(self, command, params):
738 """
739 Parse the .bb files
740 """
741 if bb.fetch.fetcher_compare_revisions(command.cooker.data):
742 command.finishAsyncCommand(code=1)
743 else:
744 command.finishAsyncCommand()
745 compareRevisions.needcache = True
746
747 def triggerEvent(self, command, params):
748 """
749 Trigger a certain event
750 """
751 event = params[0]
752 bb.event.fire(eval(event), command.cooker.data)
Andrew Geissler517393d2023-01-13 08:55:19 -0600753 process_server.clear_async_cmd()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500754 triggerEvent.needcache = False
755
756 def resetCooker(self, command, params):
757 """
758 Reset the cooker to its initial state, thus forcing a reparse for
759 any async command that has the needcache property set to True
760 """
761 command.cooker.reset()
762 command.finishAsyncCommand()
763 resetCooker.needcache = False
764
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500765 def clientComplete(self, command, params):
766 """
767 Do the right thing when the controlling client exits
768 """
769 command.cooker.clientComplete()
770 command.finishAsyncCommand()
771 clientComplete.needcache = False
772
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500773 def findSigInfo(self, command, params):
774 """
775 Find signature info files via the signature generator
776 """
Andrew Geissler635e0e42020-08-21 15:58:33 -0500777 (mc, pn) = bb.runqueue.split_mc(params[0])
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500778 taskname = params[1]
779 sigs = params[2]
Patrick Williams169d7bc2024-01-05 11:33:25 -0600780 bb.siggen.check_siggen_version(bb.siggen)
Andrew Geissler635e0e42020-08-21 15:58:33 -0500781 res = bb.siggen.find_siginfo(pn, taskname, sigs, command.cooker.databuilder.mcdata[mc])
782 bb.event.fire(bb.event.FindSigInfoResult(res), command.cooker.databuilder.mcdata[mc])
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500783 command.finishAsyncCommand()
784 findSigInfo.needcache = False
Andrew Geissler220dafd2023-10-04 10:18:08 -0500785
786 def getTaskSignatures(self, command, params):
787 res = command.cooker.getTaskSignatures(params[0], params[1])
788 bb.event.fire(bb.event.GetTaskSignatureResult(res), command.cooker.data)
789 command.finishAsyncCommand()
790 getTaskSignatures.needcache = True