summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/bb/command.py
diff options
context:
space:
mode:
Diffstat (limited to 'bitbake/lib/bb/command.py')
-rw-r--r--bitbake/lib/bb/command.py112
1 files changed, 79 insertions, 33 deletions
diff --git a/bitbake/lib/bb/command.py b/bitbake/lib/bb/command.py
index dd77cdd6e2..1fcb9bf14c 100644
--- a/bitbake/lib/bb/command.py
+++ b/bitbake/lib/bb/command.py
@@ -20,6 +20,7 @@ Commands are queued in a CommandQueue
20 20
21from collections import OrderedDict, defaultdict 21from collections import OrderedDict, defaultdict
22 22
23import io
23import bb.event 24import bb.event
24import bb.cooker 25import bb.cooker
25import bb.remotedata 26import bb.remotedata
@@ -50,23 +51,32 @@ class Command:
50 """ 51 """
51 A queue of asynchronous commands for bitbake 52 A queue of asynchronous commands for bitbake
52 """ 53 """
53 def __init__(self, cooker): 54 def __init__(self, cooker, process_server):
54 self.cooker = cooker 55 self.cooker = cooker
55 self.cmds_sync = CommandsSync() 56 self.cmds_sync = CommandsSync()
56 self.cmds_async = CommandsAsync() 57 self.cmds_async = CommandsAsync()
57 self.remotedatastores = None 58 self.remotedatastores = None
58 59
59 # FIXME Add lock for this 60 self.process_server = process_server
61 # Access with locking using process_server.{get/set/clear}_async_cmd()
60 self.currentAsyncCommand = None 62 self.currentAsyncCommand = None
61 63
62 def runCommand(self, commandline, ro_only = False): 64 def runCommand(self, commandline, process_server, ro_only=False):
63 command = commandline.pop(0) 65 command = commandline.pop(0)
64 66
65 # Ensure cooker is ready for commands 67 # Ensure cooker is ready for commands
66 if command != "updateConfig" and command != "setFeatures": 68 if command not in ["updateConfig", "setFeatures", "ping"]:
67 self.cooker.init_configdata() 69 try:
68 if not self.remotedatastores: 70 self.cooker.init_configdata()
69 self.remotedatastores = bb.remotedata.RemoteDatastores(self.cooker) 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()
70 80
71 if hasattr(CommandsSync, command): 81 if hasattr(CommandsSync, command):
72 # Can run synchronous commands straight away 82 # Can run synchronous commands straight away
@@ -75,7 +85,6 @@ class Command:
75 if not hasattr(command_method, 'readonly') or not getattr(command_method, 'readonly'): 85 if not hasattr(command_method, 'readonly') or not getattr(command_method, 'readonly'):
76 return None, "Not able to execute not readonly commands in readonly mode" 86 return None, "Not able to execute not readonly commands in readonly mode"
77 try: 87 try:
78 self.cooker.process_inotify_updates()
79 if getattr(command_method, 'needconfig', True): 88 if getattr(command_method, 'needconfig', True):
80 self.cooker.updateCacheSync() 89 self.cooker.updateCacheSync()
81 result = command_method(self, commandline) 90 result = command_method(self, commandline)
@@ -90,24 +99,23 @@ class Command:
90 return None, traceback.format_exc() 99 return None, traceback.format_exc()
91 else: 100 else:
92 return result, None 101 return result, None
93 if self.currentAsyncCommand is not None:
94 return None, "Busy (%s in progress)" % self.currentAsyncCommand[0]
95 if command not in CommandsAsync.__dict__: 102 if command not in CommandsAsync.__dict__:
96 return None, "No such command" 103 return None, "No such command"
97 self.currentAsyncCommand = (command, commandline) 104 if not process_server.set_async_cmd((command, commandline)):
98 self.cooker.idleCallBackRegister(self.cooker.runCommands, self.cooker) 105 return None, "Busy (%s in progress)" % self.process_server.get_async_cmd()[0]
106 self.cooker.idleCallBackRegister(self.runAsyncCommand, process_server)
99 return True, None 107 return True, None
100 108
101 def runAsyncCommand(self): 109 def runAsyncCommand(self, _, process_server, halt):
102 try: 110 try:
103 self.cooker.process_inotify_updates()
104 if self.cooker.state in (bb.cooker.state.error, bb.cooker.state.shutdown, bb.cooker.state.forceshutdown): 111 if self.cooker.state in (bb.cooker.state.error, bb.cooker.state.shutdown, bb.cooker.state.forceshutdown):
105 # updateCache will trigger a shutdown of the parser 112 # updateCache will trigger a shutdown of the parser
106 # and then raise BBHandledException triggering an exit 113 # and then raise BBHandledException triggering an exit
107 self.cooker.updateCache() 114 self.cooker.updateCache()
108 return False 115 return bb.server.process.idleFinish("Cooker in error state")
109 if self.currentAsyncCommand is not None: 116 cmd = process_server.get_async_cmd()
110 (command, options) = self.currentAsyncCommand 117 if cmd is not None:
118 (command, options) = cmd
111 commandmethod = getattr(CommandsAsync, command) 119 commandmethod = getattr(CommandsAsync, command)
112 needcache = getattr( commandmethod, "needcache" ) 120 needcache = getattr( commandmethod, "needcache" )
113 if needcache and self.cooker.state != bb.cooker.state.running: 121 if needcache and self.cooker.state != bb.cooker.state.running:
@@ -117,24 +125,21 @@ class Command:
117 commandmethod(self.cmds_async, self, options) 125 commandmethod(self.cmds_async, self, options)
118 return False 126 return False
119 else: 127 else:
120 return False 128 return bb.server.process.idleFinish("Nothing to do, no async command?")
121 except KeyboardInterrupt as exc: 129 except KeyboardInterrupt as exc:
122 self.finishAsyncCommand("Interrupted") 130 return bb.server.process.idleFinish("Interrupted")
123 return False
124 except SystemExit as exc: 131 except SystemExit as exc:
125 arg = exc.args[0] 132 arg = exc.args[0]
126 if isinstance(arg, str): 133 if isinstance(arg, str):
127 self.finishAsyncCommand(arg) 134 return bb.server.process.idleFinish(arg)
128 else: 135 else:
129 self.finishAsyncCommand("Exited with %s" % arg) 136 return bb.server.process.idleFinish("Exited with %s" % arg)
130 return False
131 except Exception as exc: 137 except Exception as exc:
132 import traceback 138 import traceback
133 if isinstance(exc, bb.BBHandledException): 139 if isinstance(exc, bb.BBHandledException):
134 self.finishAsyncCommand("") 140 return bb.server.process.idleFinish("")
135 else: 141 else:
136 self.finishAsyncCommand(traceback.format_exc()) 142 return bb.server.process.idleFinish(traceback.format_exc())
137 return False
138 143
139 def finishAsyncCommand(self, msg=None, code=None): 144 def finishAsyncCommand(self, msg=None, code=None):
140 if msg or msg == "": 145 if msg or msg == "":
@@ -143,8 +148,8 @@ class Command:
143 bb.event.fire(CommandExit(code), self.cooker.data) 148 bb.event.fire(CommandExit(code), self.cooker.data)
144 else: 149 else:
145 bb.event.fire(CommandCompleted(), self.cooker.data) 150 bb.event.fire(CommandCompleted(), self.cooker.data)
146 self.currentAsyncCommand = None
147 self.cooker.finishcommand() 151 self.cooker.finishcommand()
152 self.process_server.clear_async_cmd()
148 153
149 def reset(self): 154 def reset(self):
150 if self.remotedatastores: 155 if self.remotedatastores:
@@ -157,6 +162,14 @@ class CommandsSync:
157 These must not influence any running synchronous command. 162 These must not influence any running synchronous command.
158 """ 163 """
159 164
165 def ping(self, command, params):
166 """
167 Allow a UI to check the server is still alive
168 """
169 return "Still alive!"
170 ping.needconfig = False
171 ping.readonly = True
172
160 def stateShutdown(self, command, params): 173 def stateShutdown(self, command, params):
161 """ 174 """
162 Trigger cooker 'shutdown' mode 175 Trigger cooker 'shutdown' mode
@@ -294,6 +307,11 @@ class CommandsSync:
294 return ret 307 return ret
295 getLayerPriorities.readonly = True 308 getLayerPriorities.readonly = True
296 309
310 def revalidateCaches(self, command, params):
311 """Called by UI clients when metadata may have changed"""
312 command.cooker.revalidateCaches()
313 parseConfiguration.needconfig = False
314
297 def getRecipes(self, command, params): 315 def getRecipes(self, command, params):
298 try: 316 try:
299 mc = params[0] 317 mc = params[0]
@@ -500,6 +518,17 @@ class CommandsSync:
500 d = command.remotedatastores[dsindex].varhistory 518 d = command.remotedatastores[dsindex].varhistory
501 return getattr(d, method)(*args, **kwargs) 519 return getattr(d, method)(*args, **kwargs)
502 520
521 def dataStoreConnectorVarHistCmdEmit(self, command, params):
522 dsindex = params[0]
523 var = params[1]
524 oval = params[2]
525 val = params[3]
526 d = command.remotedatastores[params[4]]
527
528 o = io.StringIO()
529 command.remotedatastores[dsindex].varhistory.emit(var, oval, val, o, d)
530 return o.getvalue()
531
503 def dataStoreConnectorIncHistCmd(self, command, params): 532 def dataStoreConnectorIncHistCmd(self, command, params):
504 dsindex = params[0] 533 dsindex = params[0]
505 method = params[1] 534 method = params[1]
@@ -521,8 +550,8 @@ class CommandsSync:
521 and return a datastore object representing the environment 550 and return a datastore object representing the environment
522 for the recipe. 551 for the recipe.
523 """ 552 """
524 fn = params[0] 553 virtualfn = params[0]
525 mc = bb.runqueue.mc_from_tid(fn) 554 (fn, cls, mc) = bb.cache.virtualfn2realfn(virtualfn)
526 appends = params[1] 555 appends = params[1]
527 appendlist = params[2] 556 appendlist = params[2]
528 if len(params) > 3: 557 if len(params) > 3:
@@ -537,6 +566,7 @@ class CommandsSync:
537 appendfiles = command.cooker.collections[mc].get_file_appends(fn) 566 appendfiles = command.cooker.collections[mc].get_file_appends(fn)
538 else: 567 else:
539 appendfiles = [] 568 appendfiles = []
569 layername = command.cooker.collections[mc].calc_bbfile_priority(fn)[2]
540 # We are calling bb.cache locally here rather than on the server, 570 # We are calling bb.cache locally here rather than on the server,
541 # but that's OK because it doesn't actually need anything from 571 # but that's OK because it doesn't actually need anything from
542 # the server barring the global datastore (which we have a remote 572 # the server barring the global datastore (which we have a remote
@@ -544,11 +574,10 @@ class CommandsSync:
544 if config_data: 574 if config_data:
545 # We have to use a different function here if we're passing in a datastore 575 # We have to use a different function here if we're passing in a datastore
546 # NOTE: we took a copy above, so we don't do it here again 576 # NOTE: we took a copy above, so we don't do it here again
547 envdata = bb.cache.parse_recipe(config_data, fn, appendfiles, mc)[''] 577 envdata = command.cooker.databuilder._parse_recipe(config_data, fn, appendfiles, mc, layername)[cls]
548 else: 578 else:
549 # Use the standard path 579 # Use the standard path
550 parser = bb.cache.NoCache(command.cooker.databuilder) 580 envdata = command.cooker.databuilder.parseRecipe(virtualfn, appendfiles, layername)
551 envdata = parser.loadDataFull(fn, appendfiles)
552 idx = command.remotedatastores.store(envdata) 581 idx = command.remotedatastores.store(envdata)
553 return DataStoreConnectionHandle(idx) 582 return DataStoreConnectionHandle(idx)
554 parseRecipeFile.readonly = True 583 parseRecipeFile.readonly = True
@@ -647,6 +676,16 @@ class CommandsAsync:
647 command.finishAsyncCommand() 676 command.finishAsyncCommand()
648 findFilesMatchingInDir.needcache = False 677 findFilesMatchingInDir.needcache = False
649 678
679 def testCookerCommandEvent(self, command, params):
680 """
681 Dummy command used by OEQA selftest to test tinfoil without IO
682 """
683 pattern = params[0]
684
685 command.cooker.testCookerCommandEvent(pattern)
686 command.finishAsyncCommand()
687 testCookerCommandEvent.needcache = False
688
650 def findConfigFilePath(self, command, params): 689 def findConfigFilePath(self, command, params):
651 """ 690 """
652 Find the path of the requested configuration file 691 Find the path of the requested configuration file
@@ -711,7 +750,7 @@ class CommandsAsync:
711 """ 750 """
712 event = params[0] 751 event = params[0]
713 bb.event.fire(eval(event), command.cooker.data) 752 bb.event.fire(eval(event), command.cooker.data)
714 command.currentAsyncCommand = None 753 process_server.clear_async_cmd()
715 triggerEvent.needcache = False 754 triggerEvent.needcache = False
716 755
717 def resetCooker(self, command, params): 756 def resetCooker(self, command, params):
@@ -738,7 +777,14 @@ class CommandsAsync:
738 (mc, pn) = bb.runqueue.split_mc(params[0]) 777 (mc, pn) = bb.runqueue.split_mc(params[0])
739 taskname = params[1] 778 taskname = params[1]
740 sigs = params[2] 779 sigs = params[2]
780 bb.siggen.check_siggen_version(bb.siggen)
741 res = bb.siggen.find_siginfo(pn, taskname, sigs, command.cooker.databuilder.mcdata[mc]) 781 res = bb.siggen.find_siginfo(pn, taskname, sigs, command.cooker.databuilder.mcdata[mc])
742 bb.event.fire(bb.event.FindSigInfoResult(res), command.cooker.databuilder.mcdata[mc]) 782 bb.event.fire(bb.event.FindSigInfoResult(res), command.cooker.databuilder.mcdata[mc])
743 command.finishAsyncCommand() 783 command.finishAsyncCommand()
744 findSigInfo.needcache = False 784 findSigInfo.needcache = False
785
786 def getTaskSignatures(self, command, params):
787 res = command.cooker.getTaskSignatures(params[0], params[1])
788 bb.event.fire(bb.event.GetTaskSignatureResult(res), command.cooker.data)
789 command.finishAsyncCommand()
790 getTaskSignatures.needcache = True