diff options
Diffstat (limited to 'bitbake/lib/bb/command.py')
| -rw-r--r-- | bitbake/lib/bb/command.py | 813 |
1 files changed, 0 insertions, 813 deletions
diff --git a/bitbake/lib/bb/command.py b/bitbake/lib/bb/command.py deleted file mode 100644 index 59a979ee90..0000000000 --- a/bitbake/lib/bb/command.py +++ /dev/null | |||
| @@ -1,813 +0,0 @@ | |||
| 1 | """ | ||
| 2 | BitBake 'Command' module | ||
| 3 | |||
| 4 | Provide an interface to interact with the bitbake server through 'commands' | ||
| 5 | """ | ||
| 6 | |||
| 7 | # Copyright (C) 2006-2007 Richard Purdie | ||
| 8 | # | ||
| 9 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 10 | # | ||
| 11 | |||
| 12 | """ | ||
| 13 | The bitbake server takes 'commands' from its UI/commandline. | ||
| 14 | Commands are either synchronous or asynchronous. | ||
| 15 | Async commands return data to the client in the form of events. | ||
| 16 | Sync commands must only return data through the function return value | ||
| 17 | and must not trigger events, directly or indirectly. | ||
| 18 | Commands are queued in a CommandQueue | ||
| 19 | """ | ||
| 20 | |||
| 21 | from collections import OrderedDict, defaultdict | ||
| 22 | |||
| 23 | import io | ||
| 24 | import bb.event | ||
| 25 | import bb.cooker | ||
| 26 | import bb.remotedata | ||
| 27 | import bb.parse | ||
| 28 | |||
| 29 | class DataStoreConnectionHandle(object): | ||
| 30 | def __init__(self, dsindex=0): | ||
| 31 | self.dsindex = dsindex | ||
| 32 | |||
| 33 | class CommandCompleted(bb.event.Event): | ||
| 34 | pass | ||
| 35 | |||
| 36 | class CommandExit(bb.event.Event): | ||
| 37 | def __init__(self, exitcode): | ||
| 38 | bb.event.Event.__init__(self) | ||
| 39 | self.exitcode = int(exitcode) | ||
| 40 | |||
| 41 | class CommandFailed(CommandExit): | ||
| 42 | def __init__(self, message): | ||
| 43 | self.error = message | ||
| 44 | CommandExit.__init__(self, 1) | ||
| 45 | def __str__(self): | ||
| 46 | return "Command execution failed: %s" % self.error | ||
| 47 | |||
| 48 | class CommandError(Exception): | ||
| 49 | pass | ||
| 50 | |||
| 51 | class Command: | ||
| 52 | """ | ||
| 53 | A queue of asynchronous commands for bitbake | ||
| 54 | """ | ||
| 55 | def __init__(self, cooker, process_server): | ||
| 56 | self.cooker = cooker | ||
| 57 | self.cmds_sync = CommandsSync() | ||
| 58 | self.cmds_async = CommandsAsync() | ||
| 59 | self.remotedatastores = None | ||
| 60 | |||
| 61 | self.process_server = process_server | ||
| 62 | # Access with locking using process_server.{get/set/clear}_async_cmd() | ||
| 63 | self.currentAsyncCommand = None | ||
| 64 | |||
| 65 | def runCommand(self, commandline, process_server, ro_only=False): | ||
| 66 | command = commandline.pop(0) | ||
| 67 | |||
| 68 | # Ensure cooker is ready for commands | ||
| 69 | if command not in ["updateConfig", "setFeatures", "ping"]: | ||
| 70 | try: | ||
| 71 | self.cooker.init_configdata() | ||
| 72 | if not self.remotedatastores: | ||
| 73 | self.remotedatastores = bb.remotedata.RemoteDatastores(self.cooker) | ||
| 74 | except (Exception, SystemExit) as exc: | ||
| 75 | import traceback | ||
| 76 | if isinstance(exc, bb.BBHandledException): | ||
| 77 | # We need to start returning real exceptions here. Until we do, we can't | ||
| 78 | # tell if an exception is an instance of bb.BBHandledException | ||
| 79 | return None, "bb.BBHandledException()\n" + traceback.format_exc() | ||
| 80 | return None, traceback.format_exc() | ||
| 81 | |||
| 82 | if hasattr(CommandsSync, command): | ||
| 83 | # Can run synchronous commands straight away | ||
| 84 | command_method = getattr(self.cmds_sync, command) | ||
| 85 | if ro_only: | ||
| 86 | if not hasattr(command_method, 'readonly') or not getattr(command_method, 'readonly'): | ||
| 87 | return None, "Not able to execute not readonly commands in readonly mode" | ||
| 88 | try: | ||
| 89 | if getattr(command_method, 'needconfig', True): | ||
| 90 | self.cooker.updateCacheSync() | ||
| 91 | result = command_method(self, commandline) | ||
| 92 | except CommandError as exc: | ||
| 93 | return None, exc.args[0] | ||
| 94 | except (Exception, SystemExit) as exc: | ||
| 95 | import traceback | ||
| 96 | 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() | ||
| 100 | return None, traceback.format_exc() | ||
| 101 | else: | ||
| 102 | return result, None | ||
| 103 | if command not in CommandsAsync.__dict__: | ||
| 104 | return None, "No such command" | ||
| 105 | 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) | ||
| 108 | return True, None | ||
| 109 | |||
| 110 | def runAsyncCommand(self, _, process_server, halt): | ||
| 111 | try: | ||
| 112 | if self.cooker.state in (bb.cooker.State.ERROR, bb.cooker.State.SHUTDOWN, bb.cooker.State.FORCE_SHUTDOWN): | ||
| 113 | # updateCache will trigger a shutdown of the parser | ||
| 114 | # and then raise BBHandledException triggering an exit | ||
| 115 | self.cooker.updateCache() | ||
| 116 | return bb.server.process.idleFinish("Cooker in error state") | ||
| 117 | cmd = process_server.get_async_cmd() | ||
| 118 | if cmd is not None: | ||
| 119 | (command, options) = cmd | ||
| 120 | commandmethod = getattr(CommandsAsync, command) | ||
| 121 | needcache = getattr( commandmethod, "needcache" ) | ||
| 122 | if needcache and self.cooker.state != bb.cooker.State.RUNNING: | ||
| 123 | self.cooker.updateCache() | ||
| 124 | return True | ||
| 125 | else: | ||
| 126 | commandmethod(self.cmds_async, self, options) | ||
| 127 | return False | ||
| 128 | else: | ||
| 129 | return bb.server.process.idleFinish("Nothing to do, no async command?") | ||
| 130 | except KeyboardInterrupt as exc: | ||
| 131 | return bb.server.process.idleFinish("Interrupted") | ||
| 132 | except SystemExit as exc: | ||
| 133 | arg = exc.args[0] | ||
| 134 | if isinstance(arg, str): | ||
| 135 | return bb.server.process.idleFinish(arg) | ||
| 136 | else: | ||
| 137 | return bb.server.process.idleFinish("Exited with %s" % arg) | ||
| 138 | except Exception as exc: | ||
| 139 | import traceback | ||
| 140 | if isinstance(exc, bb.BBHandledException): | ||
| 141 | return bb.server.process.idleFinish("") | ||
| 142 | else: | ||
| 143 | return bb.server.process.idleFinish(traceback.format_exc()) | ||
| 144 | |||
| 145 | def finishAsyncCommand(self, msg=None, code=None): | ||
| 146 | self.cooker.finishcommand() | ||
| 147 | self.process_server.clear_async_cmd() | ||
| 148 | if msg or msg == "": | ||
| 149 | bb.event.fire(CommandFailed(msg), self.cooker.data) | ||
| 150 | elif code: | ||
| 151 | bb.event.fire(CommandExit(code), self.cooker.data) | ||
| 152 | else: | ||
| 153 | bb.event.fire(CommandCompleted(), self.cooker.data) | ||
| 154 | |||
| 155 | def reset(self): | ||
| 156 | if self.remotedatastores: | ||
| 157 | self.remotedatastores = bb.remotedata.RemoteDatastores(self.cooker) | ||
| 158 | |||
| 159 | class CommandsSync: | ||
| 160 | """ | ||
| 161 | A class of synchronous commands | ||
| 162 | These should run quickly so as not to hurt interactive performance. | ||
| 163 | These must not influence any running synchronous command. | ||
| 164 | """ | ||
| 165 | |||
| 166 | def ping(self, command, params): | ||
| 167 | """ | ||
| 168 | Allow a UI to check the server is still alive | ||
| 169 | """ | ||
| 170 | return "Still alive!" | ||
| 171 | ping.needconfig = False | ||
| 172 | ping.readonly = True | ||
| 173 | |||
| 174 | def stateShutdown(self, command, params): | ||
| 175 | """ | ||
| 176 | Trigger cooker 'shutdown' mode | ||
| 177 | """ | ||
| 178 | command.cooker.shutdown(False) | ||
| 179 | |||
| 180 | def stateForceShutdown(self, command, params): | ||
| 181 | """ | ||
| 182 | Stop the cooker | ||
| 183 | """ | ||
| 184 | command.cooker.shutdown(True) | ||
| 185 | |||
| 186 | def getAllKeysWithFlags(self, command, params): | ||
| 187 | """ | ||
| 188 | Returns a dump of the global state. Call with | ||
| 189 | variable flags to be retrieved as params. | ||
| 190 | """ | ||
| 191 | flaglist = params[0] | ||
| 192 | return command.cooker.getAllKeysWithFlags(flaglist) | ||
| 193 | getAllKeysWithFlags.readonly = True | ||
| 194 | |||
| 195 | def getVariable(self, command, params): | ||
| 196 | """ | ||
| 197 | Read the value of a variable from data | ||
| 198 | """ | ||
| 199 | varname = params[0] | ||
| 200 | expand = True | ||
| 201 | if len(params) > 1: | ||
| 202 | expand = (params[1] == "True") | ||
| 203 | |||
| 204 | return command.cooker.data.getVar(varname, expand) | ||
| 205 | getVariable.readonly = True | ||
| 206 | |||
| 207 | def setVariable(self, command, params): | ||
| 208 | """ | ||
| 209 | Set the value of variable in data | ||
| 210 | """ | ||
| 211 | varname = params[0] | ||
| 212 | value = str(params[1]) | ||
| 213 | command.cooker.extraconfigdata[varname] = value | ||
| 214 | command.cooker.data.setVar(varname, value) | ||
| 215 | |||
| 216 | def getSetVariable(self, command, params): | ||
| 217 | """ | ||
| 218 | Read the value of a variable from data and set it into the datastore | ||
| 219 | which effectively expands and locks the value. | ||
| 220 | """ | ||
| 221 | varname = params[0] | ||
| 222 | result = self.getVariable(command, params) | ||
| 223 | command.cooker.data.setVar(varname, result) | ||
| 224 | return result | ||
| 225 | |||
| 226 | def setConfig(self, command, params): | ||
| 227 | """ | ||
| 228 | Set the value of variable in configuration | ||
| 229 | """ | ||
| 230 | varname = params[0] | ||
| 231 | value = str(params[1]) | ||
| 232 | setattr(command.cooker.configuration, varname, value) | ||
| 233 | |||
| 234 | def enableDataTracking(self, command, params): | ||
| 235 | """ | ||
| 236 | Enable history tracking for variables | ||
| 237 | """ | ||
| 238 | command.cooker.enableDataTracking() | ||
| 239 | |||
| 240 | def disableDataTracking(self, command, params): | ||
| 241 | """ | ||
| 242 | Disable history tracking for variables | ||
| 243 | """ | ||
| 244 | command.cooker.disableDataTracking() | ||
| 245 | |||
| 246 | def setPrePostConfFiles(self, command, params): | ||
| 247 | prefiles = params[0].split() | ||
| 248 | postfiles = params[1].split() | ||
| 249 | command.cooker.configuration.prefile = prefiles | ||
| 250 | command.cooker.configuration.postfile = postfiles | ||
| 251 | setPrePostConfFiles.needconfig = False | ||
| 252 | |||
| 253 | def matchFile(self, command, params): | ||
| 254 | fMatch = params[0] | ||
| 255 | try: | ||
| 256 | mc = params[0] | ||
| 257 | except IndexError: | ||
| 258 | mc = '' | ||
| 259 | return command.cooker.matchFile(fMatch, mc) | ||
| 260 | matchFile.needconfig = False | ||
| 261 | |||
| 262 | def getUIHandlerNum(self, command, params): | ||
| 263 | return bb.event.get_uihandler() | ||
| 264 | getUIHandlerNum.needconfig = False | ||
| 265 | getUIHandlerNum.readonly = True | ||
| 266 | |||
| 267 | def setEventMask(self, command, params): | ||
| 268 | handlerNum = params[0] | ||
| 269 | llevel = params[1] | ||
| 270 | debug_domains = params[2] | ||
| 271 | mask = params[3] | ||
| 272 | return bb.event.set_UIHmask(handlerNum, llevel, debug_domains, mask) | ||
| 273 | setEventMask.needconfig = False | ||
| 274 | setEventMask.readonly = True | ||
| 275 | |||
| 276 | def setFeatures(self, command, params): | ||
| 277 | """ | ||
| 278 | Set the cooker features to include the passed list of features | ||
| 279 | """ | ||
| 280 | features = params[0] | ||
| 281 | command.cooker.setFeatures(features) | ||
| 282 | setFeatures.needconfig = False | ||
| 283 | # although we change the internal state of the cooker, this is transparent since | ||
| 284 | # we always take and leave the cooker in state.initial | ||
| 285 | setFeatures.readonly = True | ||
| 286 | |||
| 287 | def updateConfig(self, command, params): | ||
| 288 | options = params[0] | ||
| 289 | environment = params[1] | ||
| 290 | cmdline = params[2] | ||
| 291 | command.cooker.updateConfigOpts(options, environment, cmdline) | ||
| 292 | updateConfig.needconfig = False | ||
| 293 | |||
| 294 | def parseConfiguration(self, command, params): | ||
| 295 | """Instruct bitbake to parse its configuration | ||
| 296 | NOTE: it is only necessary to call this if you aren't calling any normal action | ||
| 297 | (otherwise parsing is taken care of automatically) | ||
| 298 | """ | ||
| 299 | command.cooker.parseConfiguration() | ||
| 300 | parseConfiguration.needconfig = False | ||
| 301 | |||
| 302 | def getLayerPriorities(self, command, params): | ||
| 303 | command.cooker.parseConfiguration() | ||
| 304 | ret = [] | ||
| 305 | # regex objects cannot be marshalled by xmlrpc | ||
| 306 | for collection, pattern, regex, pri in command.cooker.bbfile_config_priorities: | ||
| 307 | ret.append((collection, pattern, regex.pattern, pri)) | ||
| 308 | return ret | ||
| 309 | getLayerPriorities.readonly = True | ||
| 310 | |||
| 311 | def revalidateCaches(self, command, params): | ||
| 312 | """Called by UI clients when metadata may have changed""" | ||
| 313 | command.cooker.revalidateCaches() | ||
| 314 | revalidateCaches.needconfig = False | ||
| 315 | |||
| 316 | def getRecipes(self, command, params): | ||
| 317 | try: | ||
| 318 | mc = params[0] | ||
| 319 | except IndexError: | ||
| 320 | mc = '' | ||
| 321 | return list(command.cooker.recipecaches[mc].pkg_pn.items()) | ||
| 322 | getRecipes.readonly = True | ||
| 323 | |||
| 324 | def getRecipeDepends(self, command, params): | ||
| 325 | try: | ||
| 326 | mc = params[0] | ||
| 327 | except IndexError: | ||
| 328 | mc = '' | ||
| 329 | return list(command.cooker.recipecaches[mc].deps.items()) | ||
| 330 | getRecipeDepends.readonly = True | ||
| 331 | |||
| 332 | def getRecipeVersions(self, command, params): | ||
| 333 | try: | ||
| 334 | mc = params[0] | ||
| 335 | except IndexError: | ||
| 336 | mc = '' | ||
| 337 | return command.cooker.recipecaches[mc].pkg_pepvpr | ||
| 338 | getRecipeVersions.readonly = True | ||
| 339 | |||
| 340 | def getRecipeProvides(self, command, params): | ||
| 341 | try: | ||
| 342 | mc = params[0] | ||
| 343 | except IndexError: | ||
| 344 | mc = '' | ||
| 345 | return command.cooker.recipecaches[mc].fn_provides | ||
| 346 | getRecipeProvides.readonly = True | ||
| 347 | |||
| 348 | def getRecipePackages(self, command, params): | ||
| 349 | try: | ||
| 350 | mc = params[0] | ||
| 351 | except IndexError: | ||
| 352 | mc = '' | ||
| 353 | return command.cooker.recipecaches[mc].packages | ||
| 354 | getRecipePackages.readonly = True | ||
| 355 | |||
| 356 | def getRecipePackagesDynamic(self, command, params): | ||
| 357 | try: | ||
| 358 | mc = params[0] | ||
| 359 | except IndexError: | ||
| 360 | mc = '' | ||
| 361 | return command.cooker.recipecaches[mc].packages_dynamic | ||
| 362 | getRecipePackagesDynamic.readonly = True | ||
| 363 | |||
| 364 | def getRProviders(self, command, params): | ||
| 365 | try: | ||
| 366 | mc = params[0] | ||
| 367 | except IndexError: | ||
| 368 | mc = '' | ||
| 369 | return command.cooker.recipecaches[mc].rproviders | ||
| 370 | getRProviders.readonly = True | ||
| 371 | |||
| 372 | def getRuntimeDepends(self, command, params): | ||
| 373 | ret = [] | ||
| 374 | try: | ||
| 375 | mc = params[0] | ||
| 376 | except IndexError: | ||
| 377 | mc = '' | ||
| 378 | rundeps = command.cooker.recipecaches[mc].rundeps | ||
| 379 | for key, value in rundeps.items(): | ||
| 380 | if isinstance(value, defaultdict): | ||
| 381 | value = dict(value) | ||
| 382 | ret.append((key, value)) | ||
| 383 | return ret | ||
| 384 | getRuntimeDepends.readonly = True | ||
| 385 | |||
| 386 | def getRuntimeRecommends(self, command, params): | ||
| 387 | ret = [] | ||
| 388 | try: | ||
| 389 | mc = params[0] | ||
| 390 | except IndexError: | ||
| 391 | mc = '' | ||
| 392 | runrecs = command.cooker.recipecaches[mc].runrecs | ||
| 393 | for key, value in runrecs.items(): | ||
| 394 | if isinstance(value, defaultdict): | ||
| 395 | value = dict(value) | ||
| 396 | ret.append((key, value)) | ||
| 397 | return ret | ||
| 398 | getRuntimeRecommends.readonly = True | ||
| 399 | |||
| 400 | def getRecipeInherits(self, command, params): | ||
| 401 | try: | ||
| 402 | mc = params[0] | ||
| 403 | except IndexError: | ||
| 404 | mc = '' | ||
| 405 | return command.cooker.recipecaches[mc].inherits | ||
| 406 | getRecipeInherits.readonly = True | ||
| 407 | |||
| 408 | def getBbFilePriority(self, command, params): | ||
| 409 | try: | ||
| 410 | mc = params[0] | ||
| 411 | except IndexError: | ||
| 412 | mc = '' | ||
| 413 | return command.cooker.recipecaches[mc].bbfile_priority | ||
| 414 | getBbFilePriority.readonly = True | ||
| 415 | |||
| 416 | def getDefaultPreference(self, command, params): | ||
| 417 | try: | ||
| 418 | mc = params[0] | ||
| 419 | except IndexError: | ||
| 420 | mc = '' | ||
| 421 | return command.cooker.recipecaches[mc].pkg_dp | ||
| 422 | getDefaultPreference.readonly = True | ||
| 423 | |||
| 424 | |||
| 425 | def getSkippedRecipes(self, command, params): | ||
| 426 | """ | ||
| 427 | Get the map of skipped recipes for the specified multiconfig/mc name (`params[0]`). | ||
| 428 | |||
| 429 | Invoked by `bb.tinfoil.Tinfoil.get_skipped_recipes` | ||
| 430 | |||
| 431 | :param command: Internally used parameter. | ||
| 432 | :param params: Parameter array. params[0] is multiconfig/mc name. If not given, then default mc '' is assumed. | ||
| 433 | :return: Dict whose keys are virtualfns and values are `bb.cooker.SkippedPackage` | ||
| 434 | """ | ||
| 435 | try: | ||
| 436 | mc = params[0] | ||
| 437 | except IndexError: | ||
| 438 | mc = '' | ||
| 439 | |||
| 440 | # Return list sorted by reverse priority order | ||
| 441 | import bb.cache | ||
| 442 | def sortkey(x): | ||
| 443 | vfn, _ = x | ||
| 444 | realfn, _, item_mc = bb.cache.virtualfn2realfn(vfn) | ||
| 445 | return -command.cooker.collections[item_mc].calc_bbfile_priority(realfn)[0], vfn | ||
| 446 | |||
| 447 | skipdict = OrderedDict(sorted(command.cooker.skiplist_by_mc[mc].items(), key=sortkey)) | ||
| 448 | return list(skipdict.items()) | ||
| 449 | getSkippedRecipes.readonly = True | ||
| 450 | |||
| 451 | def getOverlayedRecipes(self, command, params): | ||
| 452 | try: | ||
| 453 | mc = params[0] | ||
| 454 | except IndexError: | ||
| 455 | mc = '' | ||
| 456 | return list(command.cooker.collections[mc].overlayed.items()) | ||
| 457 | getOverlayedRecipes.readonly = True | ||
| 458 | |||
| 459 | def getFileAppends(self, command, params): | ||
| 460 | fn = params[0] | ||
| 461 | try: | ||
| 462 | mc = params[1] | ||
| 463 | except IndexError: | ||
| 464 | mc = '' | ||
| 465 | return command.cooker.collections[mc].get_file_appends(fn) | ||
| 466 | getFileAppends.readonly = True | ||
| 467 | |||
| 468 | def getAllAppends(self, command, params): | ||
| 469 | try: | ||
| 470 | mc = params[0] | ||
| 471 | except IndexError: | ||
| 472 | mc = '' | ||
| 473 | return command.cooker.collections[mc].bbappends | ||
| 474 | getAllAppends.readonly = True | ||
| 475 | |||
| 476 | def findProviders(self, command, params): | ||
| 477 | try: | ||
| 478 | mc = params[0] | ||
| 479 | except IndexError: | ||
| 480 | mc = '' | ||
| 481 | return command.cooker.findProviders(mc) | ||
| 482 | findProviders.readonly = True | ||
| 483 | |||
| 484 | def findBestProvider(self, command, params): | ||
| 485 | (mc, pn) = bb.runqueue.split_mc(params[0]) | ||
| 486 | return command.cooker.findBestProvider(pn, mc) | ||
| 487 | findBestProvider.readonly = True | ||
| 488 | |||
| 489 | def allProviders(self, command, params): | ||
| 490 | try: | ||
| 491 | mc = params[0] | ||
| 492 | except IndexError: | ||
| 493 | mc = '' | ||
| 494 | return list(bb.providers.allProviders(command.cooker.recipecaches[mc]).items()) | ||
| 495 | allProviders.readonly = True | ||
| 496 | |||
| 497 | def getRuntimeProviders(self, command, params): | ||
| 498 | rprovide = params[0] | ||
| 499 | try: | ||
| 500 | mc = params[1] | ||
| 501 | except IndexError: | ||
| 502 | mc = '' | ||
| 503 | all_p = bb.providers.getRuntimeProviders(command.cooker.recipecaches[mc], rprovide) | ||
| 504 | if all_p: | ||
| 505 | best = bb.providers.filterProvidersRunTime(all_p, rprovide, | ||
| 506 | command.cooker.data, | ||
| 507 | command.cooker.recipecaches[mc])[0][0] | ||
| 508 | else: | ||
| 509 | best = None | ||
| 510 | return all_p, best | ||
| 511 | getRuntimeProviders.readonly = True | ||
| 512 | |||
| 513 | def dataStoreConnectorCmd(self, command, params): | ||
| 514 | dsindex = params[0] | ||
| 515 | method = params[1] | ||
| 516 | args = params[2] | ||
| 517 | kwargs = params[3] | ||
| 518 | |||
| 519 | d = command.remotedatastores[dsindex] | ||
| 520 | ret = getattr(d, method)(*args, **kwargs) | ||
| 521 | |||
| 522 | if isinstance(ret, bb.data_smart.DataSmart): | ||
| 523 | idx = command.remotedatastores.store(ret) | ||
| 524 | return DataStoreConnectionHandle(idx) | ||
| 525 | |||
| 526 | return ret | ||
| 527 | |||
| 528 | def dataStoreConnectorVarHistCmd(self, command, params): | ||
| 529 | dsindex = params[0] | ||
| 530 | method = params[1] | ||
| 531 | args = params[2] | ||
| 532 | kwargs = params[3] | ||
| 533 | |||
| 534 | d = command.remotedatastores[dsindex].varhistory | ||
| 535 | return getattr(d, method)(*args, **kwargs) | ||
| 536 | |||
| 537 | def dataStoreConnectorVarHistCmdEmit(self, command, params): | ||
| 538 | dsindex = params[0] | ||
| 539 | var = params[1] | ||
| 540 | oval = params[2] | ||
| 541 | val = params[3] | ||
| 542 | d = command.remotedatastores[params[4]] | ||
| 543 | |||
| 544 | o = io.StringIO() | ||
| 545 | command.remotedatastores[dsindex].varhistory.emit(var, oval, val, o, d) | ||
| 546 | return o.getvalue() | ||
| 547 | |||
| 548 | def dataStoreConnectorIncHistCmd(self, command, params): | ||
| 549 | dsindex = params[0] | ||
| 550 | method = params[1] | ||
| 551 | args = params[2] | ||
| 552 | kwargs = params[3] | ||
| 553 | |||
| 554 | d = command.remotedatastores[dsindex].inchistory | ||
| 555 | return getattr(d, method)(*args, **kwargs) | ||
| 556 | |||
| 557 | def dataStoreConnectorRelease(self, command, params): | ||
| 558 | dsindex = params[0] | ||
| 559 | if dsindex <= 0: | ||
| 560 | raise CommandError('dataStoreConnectorRelease: invalid index %d' % dsindex) | ||
| 561 | command.remotedatastores.release(dsindex) | ||
| 562 | |||
| 563 | def parseRecipeFile(self, command, params): | ||
| 564 | """ | ||
| 565 | Parse the specified recipe file (with or without bbappends) | ||
| 566 | and return a datastore object representing the environment | ||
| 567 | for the recipe. | ||
| 568 | """ | ||
| 569 | virtualfn = params[0] | ||
| 570 | (fn, cls, mc) = bb.cache.virtualfn2realfn(virtualfn) | ||
| 571 | appends = params[1] | ||
| 572 | appendlist = params[2] | ||
| 573 | if len(params) > 3: | ||
| 574 | config_data = command.remotedatastores[params[3]] | ||
| 575 | else: | ||
| 576 | config_data = None | ||
| 577 | |||
| 578 | if appends: | ||
| 579 | if appendlist is not None: | ||
| 580 | appendfiles = appendlist | ||
| 581 | else: | ||
| 582 | appendfiles = command.cooker.collections[mc].get_file_appends(fn) | ||
| 583 | else: | ||
| 584 | appendfiles = [] | ||
| 585 | layername = command.cooker.collections[mc].calc_bbfile_priority(fn)[2] | ||
| 586 | # We are calling bb.cache locally here rather than on the server, | ||
| 587 | # but that's OK because it doesn't actually need anything from | ||
| 588 | # the server barring the global datastore (which we have a remote | ||
| 589 | # version of) | ||
| 590 | if config_data: | ||
| 591 | # We have to use a different function here if we're passing in a datastore | ||
| 592 | # NOTE: we took a copy above, so we don't do it here again | ||
| 593 | envdata = command.cooker.databuilder._parse_recipe(config_data, fn, appendfiles, mc, layername)[cls] | ||
| 594 | else: | ||
| 595 | # Use the standard path | ||
| 596 | envdata = command.cooker.databuilder.parseRecipe(virtualfn, appendfiles, layername) | ||
| 597 | idx = command.remotedatastores.store(envdata) | ||
| 598 | return DataStoreConnectionHandle(idx) | ||
| 599 | parseRecipeFile.readonly = True | ||
| 600 | |||
| 601 | def finalizeData(self, command, params): | ||
| 602 | newdata = command.cooker.data.createCopy() | ||
| 603 | bb.data.expandKeys(newdata) | ||
| 604 | bb.parse.ast.runAnonFuncs(newdata) | ||
| 605 | idx = command.remotedatastores.store(newdata) | ||
| 606 | return DataStoreConnectionHandle(idx) | ||
| 607 | |||
| 608 | class CommandsAsync: | ||
| 609 | """ | ||
| 610 | A class of asynchronous commands | ||
| 611 | These functions communicate via generated events. | ||
| 612 | Any function that requires metadata parsing should be here. | ||
| 613 | """ | ||
| 614 | |||
| 615 | def buildFile(self, command, params): | ||
| 616 | """ | ||
| 617 | Build a single specified .bb file | ||
| 618 | """ | ||
| 619 | bfile = params[0] | ||
| 620 | task = params[1] | ||
| 621 | if len(params) > 2: | ||
| 622 | internal = params[2] | ||
| 623 | else: | ||
| 624 | internal = False | ||
| 625 | |||
| 626 | if internal: | ||
| 627 | command.cooker.buildFileInternal(bfile, task, fireevents=False, quietlog=True) | ||
| 628 | else: | ||
| 629 | command.cooker.buildFile(bfile, task) | ||
| 630 | buildFile.needcache = False | ||
| 631 | |||
| 632 | def buildTargets(self, command, params): | ||
| 633 | """ | ||
| 634 | Build a set of targets | ||
| 635 | """ | ||
| 636 | pkgs_to_build = params[0] | ||
| 637 | task = params[1] | ||
| 638 | |||
| 639 | command.cooker.buildTargets(pkgs_to_build, task) | ||
| 640 | buildTargets.needcache = True | ||
| 641 | |||
| 642 | def generateDepTreeEvent(self, command, params): | ||
| 643 | """ | ||
| 644 | Generate an event containing the dependency information | ||
| 645 | """ | ||
| 646 | pkgs_to_build = params[0] | ||
| 647 | task = params[1] | ||
| 648 | |||
| 649 | command.cooker.generateDepTreeEvent(pkgs_to_build, task) | ||
| 650 | command.finishAsyncCommand() | ||
| 651 | generateDepTreeEvent.needcache = True | ||
| 652 | |||
| 653 | def generateDotGraph(self, command, params): | ||
| 654 | """ | ||
| 655 | Dump dependency information to disk as .dot files | ||
| 656 | """ | ||
| 657 | pkgs_to_build = params[0] | ||
| 658 | task = params[1] | ||
| 659 | |||
| 660 | command.cooker.generateDotGraphFiles(pkgs_to_build, task) | ||
| 661 | command.finishAsyncCommand() | ||
| 662 | generateDotGraph.needcache = True | ||
| 663 | |||
| 664 | def generateTargetsTree(self, command, params): | ||
| 665 | """ | ||
| 666 | Generate a tree of buildable targets. | ||
| 667 | If klass is provided ensure all recipes that inherit the class are | ||
| 668 | included in the package list. | ||
| 669 | If pkg_list provided use that list (plus any extras brought in by | ||
| 670 | klass) rather than generating a tree for all packages. | ||
| 671 | """ | ||
| 672 | klass = params[0] | ||
| 673 | pkg_list = params[1] | ||
| 674 | |||
| 675 | command.cooker.generateTargetsTree(klass, pkg_list) | ||
| 676 | command.finishAsyncCommand() | ||
| 677 | generateTargetsTree.needcache = True | ||
| 678 | |||
| 679 | def findConfigFiles(self, command, params): | ||
| 680 | """ | ||
| 681 | Find config files which provide appropriate values | ||
| 682 | for the passed configuration variable. i.e. MACHINE | ||
| 683 | """ | ||
| 684 | varname = params[0] | ||
| 685 | |||
| 686 | command.cooker.findConfigFiles(varname) | ||
| 687 | command.finishAsyncCommand() | ||
| 688 | findConfigFiles.needcache = False | ||
| 689 | |||
| 690 | def findFilesMatchingInDir(self, command, params): | ||
| 691 | """ | ||
| 692 | Find implementation files matching the specified pattern | ||
| 693 | in the requested subdirectory of a BBPATH | ||
| 694 | """ | ||
| 695 | pattern = params[0] | ||
| 696 | directory = params[1] | ||
| 697 | |||
| 698 | command.cooker.findFilesMatchingInDir(pattern, directory) | ||
| 699 | command.finishAsyncCommand() | ||
| 700 | findFilesMatchingInDir.needcache = False | ||
| 701 | |||
| 702 | def testCookerCommandEvent(self, command, params): | ||
| 703 | """ | ||
| 704 | Dummy command used by OEQA selftest to test tinfoil without IO | ||
| 705 | """ | ||
| 706 | pattern = params[0] | ||
| 707 | |||
| 708 | command.cooker.testCookerCommandEvent(pattern) | ||
| 709 | command.finishAsyncCommand() | ||
| 710 | testCookerCommandEvent.needcache = False | ||
| 711 | |||
| 712 | def findConfigFilePath(self, command, params): | ||
| 713 | """ | ||
| 714 | Find the path of the requested configuration file | ||
| 715 | """ | ||
| 716 | configfile = params[0] | ||
| 717 | |||
| 718 | command.cooker.findConfigFilePath(configfile) | ||
| 719 | command.finishAsyncCommand() | ||
| 720 | findConfigFilePath.needcache = False | ||
| 721 | |||
| 722 | def showVersions(self, command, params): | ||
| 723 | """ | ||
| 724 | Show the currently selected versions | ||
| 725 | """ | ||
| 726 | command.cooker.showVersions() | ||
| 727 | command.finishAsyncCommand() | ||
| 728 | showVersions.needcache = True | ||
| 729 | |||
| 730 | def showEnvironmentTarget(self, command, params): | ||
| 731 | """ | ||
| 732 | Print the environment of a target recipe | ||
| 733 | (needs the cache to work out which recipe to use) | ||
| 734 | """ | ||
| 735 | pkg = params[0] | ||
| 736 | |||
| 737 | command.cooker.showEnvironment(None, pkg) | ||
| 738 | command.finishAsyncCommand() | ||
| 739 | showEnvironmentTarget.needcache = True | ||
| 740 | |||
| 741 | def showEnvironment(self, command, params): | ||
| 742 | """ | ||
| 743 | Print the standard environment | ||
| 744 | or if specified the environment for a specified recipe | ||
| 745 | """ | ||
| 746 | bfile = params[0] | ||
| 747 | |||
| 748 | command.cooker.showEnvironment(bfile) | ||
| 749 | command.finishAsyncCommand() | ||
| 750 | showEnvironment.needcache = False | ||
| 751 | |||
| 752 | def parseFiles(self, command, params): | ||
| 753 | """ | ||
| 754 | Parse the .bb files | ||
| 755 | """ | ||
| 756 | command.cooker.updateCache() | ||
| 757 | command.finishAsyncCommand() | ||
| 758 | parseFiles.needcache = True | ||
| 759 | |||
| 760 | def compareRevisions(self, command, params): | ||
| 761 | """ | ||
| 762 | Parse the .bb files | ||
| 763 | """ | ||
| 764 | if bb.fetch.fetcher_compare_revisions(command.cooker.data): | ||
| 765 | command.finishAsyncCommand(code=1) | ||
| 766 | else: | ||
| 767 | command.finishAsyncCommand() | ||
| 768 | compareRevisions.needcache = True | ||
| 769 | |||
| 770 | def triggerEvent(self, command, params): | ||
| 771 | """ | ||
| 772 | Trigger a certain event | ||
| 773 | """ | ||
| 774 | event = params[0] | ||
| 775 | bb.event.fire(eval(event), command.cooker.data) | ||
| 776 | process_server.clear_async_cmd() | ||
| 777 | triggerEvent.needcache = False | ||
| 778 | |||
| 779 | def resetCooker(self, command, params): | ||
| 780 | """ | ||
| 781 | Reset the cooker to its initial state, thus forcing a reparse for | ||
| 782 | any async command that has the needcache property set to True | ||
| 783 | """ | ||
| 784 | command.cooker.reset() | ||
| 785 | command.finishAsyncCommand() | ||
| 786 | resetCooker.needcache = False | ||
| 787 | |||
| 788 | def clientComplete(self, command, params): | ||
| 789 | """ | ||
| 790 | Do the right thing when the controlling client exits | ||
| 791 | """ | ||
| 792 | command.cooker.clientComplete() | ||
| 793 | command.finishAsyncCommand() | ||
| 794 | clientComplete.needcache = False | ||
| 795 | |||
| 796 | def findSigInfo(self, command, params): | ||
| 797 | """ | ||
| 798 | Find signature info files via the signature generator | ||
| 799 | """ | ||
| 800 | (mc, pn) = bb.runqueue.split_mc(params[0]) | ||
| 801 | taskname = params[1] | ||
| 802 | sigs = params[2] | ||
| 803 | bb.siggen.check_siggen_version(bb.siggen) | ||
| 804 | res = bb.siggen.find_siginfo(pn, taskname, sigs, command.cooker.databuilder.mcdata[mc]) | ||
| 805 | bb.event.fire(bb.event.FindSigInfoResult(res), command.cooker.databuilder.mcdata[mc]) | ||
| 806 | command.finishAsyncCommand() | ||
| 807 | findSigInfo.needcache = False | ||
| 808 | |||
| 809 | def getTaskSignatures(self, command, params): | ||
| 810 | res = command.cooker.getTaskSignatures(params[0], params[1]) | ||
| 811 | bb.event.fire(bb.event.GetTaskSignatureResult(res), command.cooker.data) | ||
| 812 | command.finishAsyncCommand() | ||
| 813 | getTaskSignatures.needcache = True | ||
