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