blob: a355f56c60cbdeab813b47bbae379a0b6e2822be [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 = []
Patrick Williamse760df82023-05-26 11:10:49 -0500564 layername = command.cooker.collections[mc].calc_bbfile_priority(fn)[2]
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500565 # 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
Patrick Williamse760df82023-05-26 11:10:49 -0500572 envdata = command.cooker.databuilder._parse_recipe(config_data, fn, appendfiles, mc, layername)['']
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500573 else:
574 # Use the standard path
Patrick Williamse760df82023-05-26 11:10:49 -0500575 envdata = command.cooker.databuilder.parseRecipe(fn, appendfiles, layername)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500576 idx = command.remotedatastores.store(envdata)
577 return DataStoreConnectionHandle(idx)
578 parseRecipeFile.readonly = True
579
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500580class CommandsAsync:
581 """
582 A class of asynchronous commands
583 These functions communicate via generated events.
584 Any function that requires metadata parsing should be here.
585 """
586
587 def buildFile(self, command, params):
588 """
589 Build a single specified .bb file
590 """
591 bfile = params[0]
592 task = params[1]
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500593 if len(params) > 2:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500594 internal = params[2]
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500595 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500596 internal = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500597
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500598 if internal:
599 command.cooker.buildFileInternal(bfile, task, fireevents=False, quietlog=True)
600 else:
601 command.cooker.buildFile(bfile, task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500602 buildFile.needcache = False
603
604 def buildTargets(self, command, params):
605 """
606 Build a set of targets
607 """
608 pkgs_to_build = params[0]
609 task = params[1]
610
611 command.cooker.buildTargets(pkgs_to_build, task)
612 buildTargets.needcache = True
613
614 def generateDepTreeEvent(self, command, params):
615 """
616 Generate an event containing the dependency information
617 """
618 pkgs_to_build = params[0]
619 task = params[1]
620
621 command.cooker.generateDepTreeEvent(pkgs_to_build, task)
622 command.finishAsyncCommand()
623 generateDepTreeEvent.needcache = True
624
625 def generateDotGraph(self, command, params):
626 """
627 Dump dependency information to disk as .dot files
628 """
629 pkgs_to_build = params[0]
630 task = params[1]
631
632 command.cooker.generateDotGraphFiles(pkgs_to_build, task)
633 command.finishAsyncCommand()
634 generateDotGraph.needcache = True
635
636 def generateTargetsTree(self, command, params):
637 """
638 Generate a tree of buildable targets.
639 If klass is provided ensure all recipes that inherit the class are
640 included in the package list.
641 If pkg_list provided use that list (plus any extras brought in by
642 klass) rather than generating a tree for all packages.
643 """
644 klass = params[0]
645 pkg_list = params[1]
646
647 command.cooker.generateTargetsTree(klass, pkg_list)
648 command.finishAsyncCommand()
649 generateTargetsTree.needcache = True
650
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500651 def findConfigFiles(self, command, params):
652 """
653 Find config files which provide appropriate values
654 for the passed configuration variable. i.e. MACHINE
655 """
656 varname = params[0]
657
658 command.cooker.findConfigFiles(varname)
659 command.finishAsyncCommand()
660 findConfigFiles.needcache = False
661
662 def findFilesMatchingInDir(self, command, params):
663 """
664 Find implementation files matching the specified pattern
665 in the requested subdirectory of a BBPATH
666 """
667 pattern = params[0]
668 directory = params[1]
669
670 command.cooker.findFilesMatchingInDir(pattern, directory)
671 command.finishAsyncCommand()
672 findFilesMatchingInDir.needcache = False
673
Patrick Williams93c203f2021-10-06 16:15:23 -0500674 def testCookerCommandEvent(self, command, params):
675 """
676 Dummy command used by OEQA selftest to test tinfoil without IO
677 """
678 pattern = params[0]
679
680 command.cooker.testCookerCommandEvent(pattern)
681 command.finishAsyncCommand()
682 testCookerCommandEvent.needcache = False
683
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500684 def findConfigFilePath(self, command, params):
685 """
686 Find the path of the requested configuration file
687 """
688 configfile = params[0]
689
690 command.cooker.findConfigFilePath(configfile)
691 command.finishAsyncCommand()
692 findConfigFilePath.needcache = False
693
694 def showVersions(self, command, params):
695 """
696 Show the currently selected versions
697 """
698 command.cooker.showVersions()
699 command.finishAsyncCommand()
700 showVersions.needcache = True
701
702 def showEnvironmentTarget(self, command, params):
703 """
704 Print the environment of a target recipe
705 (needs the cache to work out which recipe to use)
706 """
707 pkg = params[0]
708
709 command.cooker.showEnvironment(None, pkg)
710 command.finishAsyncCommand()
711 showEnvironmentTarget.needcache = True
712
713 def showEnvironment(self, command, params):
714 """
715 Print the standard environment
716 or if specified the environment for a specified recipe
717 """
718 bfile = params[0]
719
720 command.cooker.showEnvironment(bfile)
721 command.finishAsyncCommand()
722 showEnvironment.needcache = False
723
724 def parseFiles(self, command, params):
725 """
726 Parse the .bb files
727 """
728 command.cooker.updateCache()
729 command.finishAsyncCommand()
730 parseFiles.needcache = True
731
732 def compareRevisions(self, command, params):
733 """
734 Parse the .bb files
735 """
736 if bb.fetch.fetcher_compare_revisions(command.cooker.data):
737 command.finishAsyncCommand(code=1)
738 else:
739 command.finishAsyncCommand()
740 compareRevisions.needcache = True
741
742 def triggerEvent(self, command, params):
743 """
744 Trigger a certain event
745 """
746 event = params[0]
747 bb.event.fire(eval(event), command.cooker.data)
Andrew Geissler517393d2023-01-13 08:55:19 -0600748 process_server.clear_async_cmd()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500749 triggerEvent.needcache = False
750
751 def resetCooker(self, command, params):
752 """
753 Reset the cooker to its initial state, thus forcing a reparse for
754 any async command that has the needcache property set to True
755 """
756 command.cooker.reset()
757 command.finishAsyncCommand()
758 resetCooker.needcache = False
759
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500760 def clientComplete(self, command, params):
761 """
762 Do the right thing when the controlling client exits
763 """
764 command.cooker.clientComplete()
765 command.finishAsyncCommand()
766 clientComplete.needcache = False
767
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500768 def findSigInfo(self, command, params):
769 """
770 Find signature info files via the signature generator
771 """
Andrew Geissler635e0e42020-08-21 15:58:33 -0500772 (mc, pn) = bb.runqueue.split_mc(params[0])
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500773 taskname = params[1]
774 sigs = params[2]
Andrew Geissler635e0e42020-08-21 15:58:33 -0500775 res = bb.siggen.find_siginfo(pn, taskname, sigs, command.cooker.databuilder.mcdata[mc])
776 bb.event.fire(bb.event.FindSigInfoResult(res), command.cooker.databuilder.mcdata[mc])
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500777 command.finishAsyncCommand()
778 findSigInfo.needcache = False