blob: 9e2cdc5c75c7a4a192f2eeed8c0c024b6f5d4590 [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
68 if command != "updateConfig" and command != "setFeatures":
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:
Andrew Geissler517393d2023-01-13 08:55:19 -060088 self.cooker.process_inotify_updates_apply()
Brad Bishopd7bf8c12018-02-25 22:55:05 -050089 if getattr(command_method, 'needconfig', True):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050090 self.cooker.updateCacheSync()
91 result = command_method(self, commandline)
92 except CommandError as exc:
93 return None, exc.args[0]
Andrew Geisslerf0343792020-11-18 10:42:21 -060094 except (Exception, SystemExit) as exc:
Patrick Williamsc124f4f2015-09-15 14:41:29 -050095 import traceback
Andrew Geisslerf0343792020-11-18 10:42:21 -060096 if isinstance(exc, bb.BBHandledException):
97 # We need to start returning real exceptions here. Until we do, we can't
98 # tell if an exception is an instance of bb.BBHandledException
99 return None, "bb.BBHandledException()\n" + traceback.format_exc()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500100 return None, traceback.format_exc()
101 else:
102 return result, None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500103 if command not in CommandsAsync.__dict__:
104 return None, "No such command"
Andrew Geissler517393d2023-01-13 08:55:19 -0600105 if not process_server.set_async_cmd((command, commandline)):
106 return None, "Busy (%s in progress)" % self.process_server.get_async_cmd()[0]
107 self.cooker.idleCallBackRegister(self.runAsyncCommand, process_server)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500108 return True, None
109
Andrew Geissler517393d2023-01-13 08:55:19 -0600110 def runAsyncCommand(self, _, process_server, halt):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500111 try:
Andrew Geissler517393d2023-01-13 08:55:19 -0600112 self.cooker.process_inotify_updates_apply()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500113 if self.cooker.state in (bb.cooker.state.error, bb.cooker.state.shutdown, bb.cooker.state.forceshutdown):
114 # updateCache will trigger a shutdown of the parser
115 # and then raise BBHandledException triggering an exit
116 self.cooker.updateCache()
Andrew Geissler517393d2023-01-13 08:55:19 -0600117 return bb.server.process.idleFinish("Cooker in error state")
118 cmd = process_server.get_async_cmd()
119 if cmd is not None:
120 (command, options) = cmd
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500121 commandmethod = getattr(CommandsAsync, command)
122 needcache = getattr( commandmethod, "needcache" )
123 if needcache and self.cooker.state != bb.cooker.state.running:
124 self.cooker.updateCache()
125 return True
126 else:
127 commandmethod(self.cmds_async, self, options)
128 return False
129 else:
Andrew Geissler517393d2023-01-13 08:55:19 -0600130 return bb.server.process.idleFinish("Nothing to do, no async command?")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500131 except KeyboardInterrupt as exc:
Andrew Geissler517393d2023-01-13 08:55:19 -0600132 return bb.server.process.idleFinish("Interrupted")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500133 except SystemExit as exc:
134 arg = exc.args[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600135 if isinstance(arg, str):
Andrew Geissler517393d2023-01-13 08:55:19 -0600136 return bb.server.process.idleFinish(arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500137 else:
Andrew Geissler517393d2023-01-13 08:55:19 -0600138 return bb.server.process.idleFinish("Exited with %s" % arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500139 except Exception as exc:
140 import traceback
141 if isinstance(exc, bb.BBHandledException):
Andrew Geissler517393d2023-01-13 08:55:19 -0600142 return bb.server.process.idleFinish("")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500143 else:
Andrew Geissler517393d2023-01-13 08:55:19 -0600144 return bb.server.process.idleFinish(traceback.format_exc())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500145
146 def finishAsyncCommand(self, msg=None, code=None):
147 if msg or msg == "":
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500148 bb.event.fire(CommandFailed(msg), self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500149 elif code:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500150 bb.event.fire(CommandExit(code), self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500151 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500152 bb.event.fire(CommandCompleted(), self.cooker.data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500153 self.cooker.finishcommand()
Andrew Geissler517393d2023-01-13 08:55:19 -0600154 self.process_server.clear_async_cmd()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500155
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500156 def reset(self):
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500157 if self.remotedatastores:
158 self.remotedatastores = bb.remotedata.RemoteDatastores(self.cooker)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500159
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500160class CommandsSync:
161 """
162 A class of synchronous commands
163 These should run quickly so as not to hurt interactive performance.
164 These must not influence any running synchronous command.
165 """
166
Andrew Geissler517393d2023-01-13 08:55:19 -0600167 def ping(self, command, params):
168 """
169 Allow a UI to check the server is still alive
170 """
171 return "Still alive!"
172
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
310 def getRecipes(self, command, params):
311 try:
312 mc = params[0]
313 except IndexError:
314 mc = ''
315 return list(command.cooker.recipecaches[mc].pkg_pn.items())
316 getRecipes.readonly = True
317
318 def getRecipeDepends(self, command, params):
319 try:
320 mc = params[0]
321 except IndexError:
322 mc = ''
323 return list(command.cooker.recipecaches[mc].deps.items())
324 getRecipeDepends.readonly = True
325
326 def getRecipeVersions(self, command, params):
327 try:
328 mc = params[0]
329 except IndexError:
330 mc = ''
331 return command.cooker.recipecaches[mc].pkg_pepvpr
332 getRecipeVersions.readonly = True
333
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500334 def getRecipeProvides(self, command, params):
335 try:
336 mc = params[0]
337 except IndexError:
338 mc = ''
339 return command.cooker.recipecaches[mc].fn_provides
340 getRecipeProvides.readonly = True
341
342 def getRecipePackages(self, command, params):
343 try:
344 mc = params[0]
345 except IndexError:
346 mc = ''
347 return command.cooker.recipecaches[mc].packages
348 getRecipePackages.readonly = True
349
350 def getRecipePackagesDynamic(self, command, params):
351 try:
352 mc = params[0]
353 except IndexError:
354 mc = ''
355 return command.cooker.recipecaches[mc].packages_dynamic
356 getRecipePackagesDynamic.readonly = True
357
358 def getRProviders(self, command, params):
359 try:
360 mc = params[0]
361 except IndexError:
362 mc = ''
363 return command.cooker.recipecaches[mc].rproviders
364 getRProviders.readonly = True
365
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500366 def getRuntimeDepends(self, command, params):
367 ret = []
368 try:
369 mc = params[0]
370 except IndexError:
371 mc = ''
372 rundeps = command.cooker.recipecaches[mc].rundeps
373 for key, value in rundeps.items():
374 if isinstance(value, defaultdict):
375 value = dict(value)
376 ret.append((key, value))
377 return ret
378 getRuntimeDepends.readonly = True
379
380 def getRuntimeRecommends(self, command, params):
381 ret = []
382 try:
383 mc = params[0]
384 except IndexError:
385 mc = ''
386 runrecs = command.cooker.recipecaches[mc].runrecs
387 for key, value in runrecs.items():
388 if isinstance(value, defaultdict):
389 value = dict(value)
390 ret.append((key, value))
391 return ret
392 getRuntimeRecommends.readonly = True
393
394 def getRecipeInherits(self, command, params):
395 try:
396 mc = params[0]
397 except IndexError:
398 mc = ''
399 return command.cooker.recipecaches[mc].inherits
400 getRecipeInherits.readonly = True
401
402 def getBbFilePriority(self, command, params):
403 try:
404 mc = params[0]
405 except IndexError:
406 mc = ''
407 return command.cooker.recipecaches[mc].bbfile_priority
408 getBbFilePriority.readonly = True
409
410 def getDefaultPreference(self, command, params):
411 try:
412 mc = params[0]
413 except IndexError:
414 mc = ''
415 return command.cooker.recipecaches[mc].pkg_dp
416 getDefaultPreference.readonly = True
417
418 def getSkippedRecipes(self, command, params):
419 # Return list sorted by reverse priority order
420 import bb.cache
Andrew Geissler5a43b432020-06-13 10:46:56 -0500421 def sortkey(x):
422 vfn, _ = x
423 realfn, _, mc = bb.cache.virtualfn2realfn(vfn)
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500424 return (-command.cooker.collections[mc].calc_bbfile_priority(realfn)[0], vfn)
Andrew Geissler5a43b432020-06-13 10:46:56 -0500425
426 skipdict = OrderedDict(sorted(command.cooker.skiplist.items(), key=sortkey))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500427 return list(skipdict.items())
428 getSkippedRecipes.readonly = True
429
430 def getOverlayedRecipes(self, command, params):
Andrew Geissler5a43b432020-06-13 10:46:56 -0500431 try:
432 mc = params[0]
433 except IndexError:
434 mc = ''
435 return list(command.cooker.collections[mc].overlayed.items())
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500436 getOverlayedRecipes.readonly = True
437
438 def getFileAppends(self, command, params):
439 fn = params[0]
Andrew Geissler5a43b432020-06-13 10:46:56 -0500440 try:
441 mc = params[1]
442 except IndexError:
443 mc = ''
444 return command.cooker.collections[mc].get_file_appends(fn)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500445 getFileAppends.readonly = True
446
447 def getAllAppends(self, command, params):
Andrew Geissler5a43b432020-06-13 10:46:56 -0500448 try:
449 mc = params[0]
450 except IndexError:
451 mc = ''
452 return command.cooker.collections[mc].bbappends
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500453 getAllAppends.readonly = True
454
455 def findProviders(self, command, params):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500456 try:
457 mc = params[0]
458 except IndexError:
459 mc = ''
460 return command.cooker.findProviders(mc)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500461 findProviders.readonly = True
462
463 def findBestProvider(self, command, params):
Andrew Geissler5a43b432020-06-13 10:46:56 -0500464 (mc, pn) = bb.runqueue.split_mc(params[0])
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500465 return command.cooker.findBestProvider(pn, mc)
466 findBestProvider.readonly = True
467
468 def allProviders(self, command, params):
469 try:
470 mc = params[0]
471 except IndexError:
472 mc = ''
473 return list(bb.providers.allProviders(command.cooker.recipecaches[mc]).items())
474 allProviders.readonly = True
475
476 def getRuntimeProviders(self, command, params):
477 rprovide = params[0]
478 try:
479 mc = params[1]
480 except IndexError:
481 mc = ''
482 all_p = bb.providers.getRuntimeProviders(command.cooker.recipecaches[mc], rprovide)
483 if all_p:
484 best = bb.providers.filterProvidersRunTime(all_p, rprovide,
485 command.cooker.data,
486 command.cooker.recipecaches[mc])[0][0]
487 else:
488 best = None
489 return all_p, best
490 getRuntimeProviders.readonly = True
491
Andrew Geissler82c905d2020-04-13 13:39:40 -0500492 def dataStoreConnectorCmd(self, command, params):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500493 dsindex = params[0]
Andrew Geissler82c905d2020-04-13 13:39:40 -0500494 method = params[1]
495 args = params[2]
496 kwargs = params[3]
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500497
Andrew Geissler82c905d2020-04-13 13:39:40 -0500498 d = command.remotedatastores[dsindex]
499 ret = getattr(d, method)(*args, **kwargs)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500500
Andrew Geissler82c905d2020-04-13 13:39:40 -0500501 if isinstance(ret, bb.data_smart.DataSmart):
502 idx = command.remotedatastores.store(ret)
503 return DataStoreConnectionHandle(idx)
504
505 return ret
506
507 def dataStoreConnectorVarHistCmd(self, command, params):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500508 dsindex = params[0]
Andrew Geissler82c905d2020-04-13 13:39:40 -0500509 method = params[1]
510 args = params[2]
511 kwargs = params[3]
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500512
Andrew Geissler82c905d2020-04-13 13:39:40 -0500513 d = command.remotedatastores[dsindex].varhistory
514 return getattr(d, method)(*args, **kwargs)
515
Andrew Geisslerc926e172021-05-07 16:11:35 -0500516 def dataStoreConnectorVarHistCmdEmit(self, command, params):
517 dsindex = params[0]
518 var = params[1]
519 oval = params[2]
520 val = params[3]
521 d = command.remotedatastores[params[4]]
522
523 o = io.StringIO()
524 command.remotedatastores[dsindex].varhistory.emit(var, oval, val, o, d)
525 return o.getvalue()
526
Andrew Geissler82c905d2020-04-13 13:39:40 -0500527 def dataStoreConnectorIncHistCmd(self, command, params):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500528 dsindex = params[0]
Andrew Geissler82c905d2020-04-13 13:39:40 -0500529 method = params[1]
530 args = params[2]
531 kwargs = params[3]
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500532
Andrew Geissler82c905d2020-04-13 13:39:40 -0500533 d = command.remotedatastores[dsindex].inchistory
534 return getattr(d, method)(*args, **kwargs)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500535
536 def dataStoreConnectorRelease(self, command, params):
537 dsindex = params[0]
538 if dsindex <= 0:
539 raise CommandError('dataStoreConnectorRelease: invalid index %d' % dsindex)
540 command.remotedatastores.release(dsindex)
541
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500542 def parseRecipeFile(self, command, params):
543 """
544 Parse the specified recipe file (with or without bbappends)
545 and return a datastore object representing the environment
546 for the recipe.
547 """
548 fn = params[0]
Andrew Geissler5a43b432020-06-13 10:46:56 -0500549 mc = bb.runqueue.mc_from_tid(fn)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500550 appends = params[1]
551 appendlist = params[2]
552 if len(params) > 3:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500553 config_data = command.remotedatastores[params[3]]
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500554 else:
555 config_data = None
556
557 if appends:
558 if appendlist is not None:
559 appendfiles = appendlist
560 else:
Andrew Geissler5a43b432020-06-13 10:46:56 -0500561 appendfiles = command.cooker.collections[mc].get_file_appends(fn)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500562 else:
563 appendfiles = []
564 # We are calling bb.cache locally here rather than on the server,
565 # but that's OK because it doesn't actually need anything from
566 # the server barring the global datastore (which we have a remote
567 # version of)
568 if config_data:
569 # We have to use a different function here if we're passing in a datastore
570 # NOTE: we took a copy above, so we don't do it here again
Andrew Geissler517393d2023-01-13 08:55:19 -0600571 envdata = command.cooker.databuilder._parse_recipe(config_data, fn, appendfiles, mc)['']
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500572 else:
573 # Use the standard path
Andrew Geissler517393d2023-01-13 08:55:19 -0600574 envdata = command.cooker.databuilder.parseRecipe(fn, appendfiles)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500575 idx = command.remotedatastores.store(envdata)
576 return DataStoreConnectionHandle(idx)
577 parseRecipeFile.readonly = True
578
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500579class CommandsAsync:
580 """
581 A class of asynchronous commands
582 These functions communicate via generated events.
583 Any function that requires metadata parsing should be here.
584 """
585
586 def buildFile(self, command, params):
587 """
588 Build a single specified .bb file
589 """
590 bfile = params[0]
591 task = params[1]
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500592 if len(params) > 2:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500593 internal = params[2]
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500594 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500595 internal = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500596
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500597 if internal:
598 command.cooker.buildFileInternal(bfile, task, fireevents=False, quietlog=True)
599 else:
600 command.cooker.buildFile(bfile, task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500601 buildFile.needcache = False
602
603 def buildTargets(self, command, params):
604 """
605 Build a set of targets
606 """
607 pkgs_to_build = params[0]
608 task = params[1]
609
610 command.cooker.buildTargets(pkgs_to_build, task)
611 buildTargets.needcache = True
612
613 def generateDepTreeEvent(self, command, params):
614 """
615 Generate an event containing the dependency information
616 """
617 pkgs_to_build = params[0]
618 task = params[1]
619
620 command.cooker.generateDepTreeEvent(pkgs_to_build, task)
621 command.finishAsyncCommand()
622 generateDepTreeEvent.needcache = True
623
624 def generateDotGraph(self, command, params):
625 """
626 Dump dependency information to disk as .dot files
627 """
628 pkgs_to_build = params[0]
629 task = params[1]
630
631 command.cooker.generateDotGraphFiles(pkgs_to_build, task)
632 command.finishAsyncCommand()
633 generateDotGraph.needcache = True
634
635 def generateTargetsTree(self, command, params):
636 """
637 Generate a tree of buildable targets.
638 If klass is provided ensure all recipes that inherit the class are
639 included in the package list.
640 If pkg_list provided use that list (plus any extras brought in by
641 klass) rather than generating a tree for all packages.
642 """
643 klass = params[0]
644 pkg_list = params[1]
645
646 command.cooker.generateTargetsTree(klass, pkg_list)
647 command.finishAsyncCommand()
648 generateTargetsTree.needcache = True
649
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500650 def findConfigFiles(self, command, params):
651 """
652 Find config files which provide appropriate values
653 for the passed configuration variable. i.e. MACHINE
654 """
655 varname = params[0]
656
657 command.cooker.findConfigFiles(varname)
658 command.finishAsyncCommand()
659 findConfigFiles.needcache = False
660
661 def findFilesMatchingInDir(self, command, params):
662 """
663 Find implementation files matching the specified pattern
664 in the requested subdirectory of a BBPATH
665 """
666 pattern = params[0]
667 directory = params[1]
668
669 command.cooker.findFilesMatchingInDir(pattern, directory)
670 command.finishAsyncCommand()
671 findFilesMatchingInDir.needcache = False
672
Patrick Williams93c203f2021-10-06 16:15:23 -0500673 def testCookerCommandEvent(self, command, params):
674 """
675 Dummy command used by OEQA selftest to test tinfoil without IO
676 """
677 pattern = params[0]
678
679 command.cooker.testCookerCommandEvent(pattern)
680 command.finishAsyncCommand()
681 testCookerCommandEvent.needcache = False
682
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500683 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)
Andrew Geissler517393d2023-01-13 08:55:19 -0600747 process_server.clear_async_cmd()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500748 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
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500767 def findSigInfo(self, command, params):
768 """
769 Find signature info files via the signature generator
770 """
Andrew Geissler635e0e42020-08-21 15:58:33 -0500771 (mc, pn) = bb.runqueue.split_mc(params[0])
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500772 taskname = params[1]
773 sigs = params[2]
Andrew Geissler635e0e42020-08-21 15:58:33 -0500774 res = bb.siggen.find_siginfo(pn, taskname, sigs, command.cooker.databuilder.mcdata[mc])
775 bb.event.fire(bb.event.FindSigInfoResult(res), command.cooker.databuilder.mcdata[mc])
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500776 command.finishAsyncCommand()
777 findSigInfo.needcache = False