diff options
Diffstat (limited to 'bitbake/lib/bb/command.py')
-rw-r--r-- | bitbake/lib/bb/command.py | 112 |
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 | ||
21 | from collections import OrderedDict, defaultdict | 21 | from collections import OrderedDict, defaultdict |
22 | 22 | ||
23 | import io | ||
23 | import bb.event | 24 | import bb.event |
24 | import bb.cooker | 25 | import bb.cooker |
25 | import bb.remotedata | 26 | import 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 | ||