diff options
Diffstat (limited to 'bitbake/lib/bb/event.py')
-rw-r--r-- | bitbake/lib/bb/event.py | 204 |
1 files changed, 129 insertions, 75 deletions
diff --git a/bitbake/lib/bb/event.py b/bitbake/lib/bb/event.py index 23e1f3187b..b29f0a5568 100644 --- a/bitbake/lib/bb/event.py +++ b/bitbake/lib/bb/event.py | |||
@@ -19,7 +19,6 @@ import sys | |||
19 | import threading | 19 | import threading |
20 | import traceback | 20 | import traceback |
21 | 21 | ||
22 | import bb.exceptions | ||
23 | import bb.utils | 22 | import bb.utils |
24 | 23 | ||
25 | # This is the pid for which we should generate the event. This is set when | 24 | # This is the pid for which we should generate the event. This is set when |
@@ -40,7 +39,7 @@ class HeartbeatEvent(Event): | |||
40 | """Triggered at regular time intervals of 10 seconds. Other events can fire much more often | 39 | """Triggered at regular time intervals of 10 seconds. Other events can fire much more often |
41 | (runQueueTaskStarted when there are many short tasks) or not at all for long periods | 40 | (runQueueTaskStarted when there are many short tasks) or not at all for long periods |
42 | of time (again runQueueTaskStarted, when there is just one long-running task), so this | 41 | of time (again runQueueTaskStarted, when there is just one long-running task), so this |
43 | event is more suitable for doing some task-independent work occassionally.""" | 42 | event is more suitable for doing some task-independent work occasionally.""" |
44 | def __init__(self, time): | 43 | def __init__(self, time): |
45 | Event.__init__(self) | 44 | Event.__init__(self) |
46 | self.time = time | 45 | self.time = time |
@@ -68,29 +67,39 @@ _catchall_handlers = {} | |||
68 | _eventfilter = None | 67 | _eventfilter = None |
69 | _uiready = False | 68 | _uiready = False |
70 | _thread_lock = threading.Lock() | 69 | _thread_lock = threading.Lock() |
71 | _thread_lock_enabled = False | 70 | _heartbeat_enabled = False |
72 | 71 | _should_exit = threading.Event() | |
73 | if hasattr(__builtins__, '__setitem__'): | ||
74 | builtins = __builtins__ | ||
75 | else: | ||
76 | builtins = __builtins__.__dict__ | ||
77 | 72 | ||
78 | def enable_threadlock(): | 73 | def enable_threadlock(): |
79 | global _thread_lock_enabled | 74 | # Always needed now |
80 | _thread_lock_enabled = True | 75 | return |
81 | 76 | ||
82 | def disable_threadlock(): | 77 | def disable_threadlock(): |
83 | global _thread_lock_enabled | 78 | # Always needed now |
84 | _thread_lock_enabled = False | 79 | return |
80 | |||
81 | def enable_heartbeat(): | ||
82 | global _heartbeat_enabled | ||
83 | _heartbeat_enabled = True | ||
84 | |||
85 | def disable_heartbeat(): | ||
86 | global _heartbeat_enabled | ||
87 | _heartbeat_enabled = False | ||
88 | |||
89 | # | ||
90 | # In long running code, this function should be called periodically | ||
91 | # to check if we should exit due to an interuption (.e.g Ctrl+C from the UI) | ||
92 | # | ||
93 | def check_for_interrupts(d): | ||
94 | global _should_exit | ||
95 | if _should_exit.is_set(): | ||
96 | bb.warn("Exiting due to interrupt.") | ||
97 | raise bb.BBHandledException() | ||
85 | 98 | ||
86 | def execute_handler(name, handler, event, d): | 99 | def execute_handler(name, handler, event, d): |
87 | event.data = d | 100 | event.data = d |
88 | addedd = False | ||
89 | if 'd' not in builtins: | ||
90 | builtins['d'] = d | ||
91 | addedd = True | ||
92 | try: | 101 | try: |
93 | ret = handler(event) | 102 | ret = handler(event, d) |
94 | except (bb.parse.SkipRecipe, bb.BBHandledException): | 103 | except (bb.parse.SkipRecipe, bb.BBHandledException): |
95 | raise | 104 | raise |
96 | except Exception: | 105 | except Exception: |
@@ -104,8 +113,7 @@ def execute_handler(name, handler, event, d): | |||
104 | raise | 113 | raise |
105 | finally: | 114 | finally: |
106 | del event.data | 115 | del event.data |
107 | if addedd: | 116 | |
108 | del builtins['d'] | ||
109 | 117 | ||
110 | def fire_class_handlers(event, d): | 118 | def fire_class_handlers(event, d): |
111 | if isinstance(event, logging.LogRecord): | 119 | if isinstance(event, logging.LogRecord): |
@@ -118,7 +126,7 @@ def fire_class_handlers(event, d): | |||
118 | if _eventfilter: | 126 | if _eventfilter: |
119 | if not _eventfilter(name, handler, event, d): | 127 | if not _eventfilter(name, handler, event, d): |
120 | continue | 128 | continue |
121 | if d and not name in (d.getVar("__BBHANDLERS_MC") or []): | 129 | if d is not None and not name in (d.getVar("__BBHANDLERS_MC") or set()): |
122 | continue | 130 | continue |
123 | execute_handler(name, handler, event, d) | 131 | execute_handler(name, handler, event, d) |
124 | 132 | ||
@@ -132,8 +140,14 @@ def print_ui_queue(): | |||
132 | if not _uiready: | 140 | if not _uiready: |
133 | from bb.msg import BBLogFormatter | 141 | from bb.msg import BBLogFormatter |
134 | # Flush any existing buffered content | 142 | # Flush any existing buffered content |
135 | sys.stdout.flush() | 143 | try: |
136 | sys.stderr.flush() | 144 | sys.stdout.flush() |
145 | except: | ||
146 | pass | ||
147 | try: | ||
148 | sys.stderr.flush() | ||
149 | except: | ||
150 | pass | ||
137 | stdout = logging.StreamHandler(sys.stdout) | 151 | stdout = logging.StreamHandler(sys.stdout) |
138 | stderr = logging.StreamHandler(sys.stderr) | 152 | stderr = logging.StreamHandler(sys.stderr) |
139 | formatter = BBLogFormatter("%(levelname)s: %(message)s") | 153 | formatter = BBLogFormatter("%(levelname)s: %(message)s") |
@@ -174,36 +188,38 @@ def print_ui_queue(): | |||
174 | 188 | ||
175 | def fire_ui_handlers(event, d): | 189 | def fire_ui_handlers(event, d): |
176 | global _thread_lock | 190 | global _thread_lock |
177 | global _thread_lock_enabled | ||
178 | 191 | ||
179 | if not _uiready: | 192 | if not _uiready: |
180 | # No UI handlers registered yet, queue up the messages | 193 | # No UI handlers registered yet, queue up the messages |
181 | ui_queue.append(event) | 194 | ui_queue.append(event) |
182 | return | 195 | return |
183 | 196 | ||
184 | if _thread_lock_enabled: | 197 | with bb.utils.lock_timeout_nocheck(_thread_lock) as lock: |
185 | _thread_lock.acquire() | 198 | if not lock: |
186 | 199 | # If we can't get the lock, we may be recursively called, queue and return | |
187 | errors = [] | 200 | ui_queue.append(event) |
188 | for h in _ui_handlers: | 201 | return |
189 | #print "Sending event %s" % event | ||
190 | try: | ||
191 | if not _ui_logfilters[h].filter(event): | ||
192 | continue | ||
193 | # We use pickle here since it better handles object instances | ||
194 | # which xmlrpc's marshaller does not. Events *must* be serializable | ||
195 | # by pickle. | ||
196 | if hasattr(_ui_handlers[h].event, "sendpickle"): | ||
197 | _ui_handlers[h].event.sendpickle((pickle.dumps(event))) | ||
198 | else: | ||
199 | _ui_handlers[h].event.send(event) | ||
200 | except: | ||
201 | errors.append(h) | ||
202 | for h in errors: | ||
203 | del _ui_handlers[h] | ||
204 | 202 | ||
205 | if _thread_lock_enabled: | 203 | errors = [] |
206 | _thread_lock.release() | 204 | for h in _ui_handlers: |
205 | #print "Sending event %s" % event | ||
206 | try: | ||
207 | if not _ui_logfilters[h].filter(event): | ||
208 | continue | ||
209 | # We use pickle here since it better handles object instances | ||
210 | # which xmlrpc's marshaller does not. Events *must* be serializable | ||
211 | # by pickle. | ||
212 | if hasattr(_ui_handlers[h].event, "sendpickle"): | ||
213 | _ui_handlers[h].event.sendpickle((pickle.dumps(event))) | ||
214 | else: | ||
215 | _ui_handlers[h].event.send(event) | ||
216 | except: | ||
217 | errors.append(h) | ||
218 | for h in errors: | ||
219 | del _ui_handlers[h] | ||
220 | |||
221 | while ui_queue: | ||
222 | fire_ui_handlers(ui_queue.pop(), d) | ||
207 | 223 | ||
208 | def fire(event, d): | 224 | def fire(event, d): |
209 | """Fire off an Event""" | 225 | """Fire off an Event""" |
@@ -232,26 +248,31 @@ noop = lambda _: None | |||
232 | def register(name, handler, mask=None, filename=None, lineno=None, data=None): | 248 | def register(name, handler, mask=None, filename=None, lineno=None, data=None): |
233 | """Register an Event handler""" | 249 | """Register an Event handler""" |
234 | 250 | ||
235 | if data and data.getVar("BB_CURRENT_MC"): | 251 | if data is not None and data.getVar("BB_CURRENT_MC"): |
236 | mc = data.getVar("BB_CURRENT_MC") | 252 | mc = data.getVar("BB_CURRENT_MC") |
237 | name = '%s%s' % (mc.replace('-', '_'), name) | 253 | name = '%s%s' % (mc.replace('-', '_'), name) |
238 | 254 | ||
239 | # already registered | 255 | # already registered |
240 | if name in _handlers: | 256 | if name in _handlers: |
257 | if data is not None: | ||
258 | bbhands_mc = (data.getVar("__BBHANDLERS_MC") or set()) | ||
259 | bbhands_mc.add(name) | ||
260 | data.setVar("__BBHANDLERS_MC", bbhands_mc) | ||
241 | return AlreadyRegistered | 261 | return AlreadyRegistered |
242 | 262 | ||
243 | if handler is not None: | 263 | if handler is not None: |
244 | # handle string containing python code | 264 | # handle string containing python code |
245 | if isinstance(handler, str): | 265 | if isinstance(handler, str): |
246 | tmp = "def %s(e):\n%s" % (name, handler) | 266 | tmp = "def %s(e, d):\n%s" % (name, handler) |
267 | # Inject empty lines to make code match lineno in filename | ||
268 | if lineno is not None: | ||
269 | tmp = "\n" * (lineno-1) + tmp | ||
247 | try: | 270 | try: |
248 | code = bb.methodpool.compile_cache(tmp) | 271 | code = bb.methodpool.compile_cache(tmp) |
249 | if not code: | 272 | if not code: |
250 | if filename is None: | 273 | if filename is None: |
251 | filename = "%s(e)" % name | 274 | filename = "%s(e, d)" % name |
252 | code = compile(tmp, filename, "exec", ast.PyCF_ONLY_AST) | 275 | code = compile(tmp, filename, "exec", ast.PyCF_ONLY_AST) |
253 | if lineno is not None: | ||
254 | ast.increment_lineno(code, lineno-1) | ||
255 | code = compile(code, filename, "exec") | 276 | code = compile(code, filename, "exec") |
256 | bb.methodpool.compile_cache_add(tmp, code) | 277 | bb.methodpool.compile_cache_add(tmp, code) |
257 | except SyntaxError: | 278 | except SyntaxError: |
@@ -274,16 +295,16 @@ def register(name, handler, mask=None, filename=None, lineno=None, data=None): | |||
274 | _event_handler_map[m] = {} | 295 | _event_handler_map[m] = {} |
275 | _event_handler_map[m][name] = True | 296 | _event_handler_map[m][name] = True |
276 | 297 | ||
277 | if data: | 298 | if data is not None: |
278 | bbhands_mc = (data.getVar("__BBHANDLERS_MC") or []) | 299 | bbhands_mc = (data.getVar("__BBHANDLERS_MC") or set()) |
279 | bbhands_mc.append(name) | 300 | bbhands_mc.add(name) |
280 | data.setVar("__BBHANDLERS_MC", bbhands_mc) | 301 | data.setVar("__BBHANDLERS_MC", bbhands_mc) |
281 | 302 | ||
282 | return Registered | 303 | return Registered |
283 | 304 | ||
284 | def remove(name, handler, data=None): | 305 | def remove(name, handler, data=None): |
285 | """Remove an Event handler""" | 306 | """Remove an Event handler""" |
286 | if data: | 307 | if data is not None: |
287 | if data.getVar("BB_CURRENT_MC"): | 308 | if data.getVar("BB_CURRENT_MC"): |
288 | mc = data.getVar("BB_CURRENT_MC") | 309 | mc = data.getVar("BB_CURRENT_MC") |
289 | name = '%s%s' % (mc.replace('-', '_'), name) | 310 | name = '%s%s' % (mc.replace('-', '_'), name) |
@@ -295,8 +316,8 @@ def remove(name, handler, data=None): | |||
295 | if name in _event_handler_map[event]: | 316 | if name in _event_handler_map[event]: |
296 | _event_handler_map[event].pop(name) | 317 | _event_handler_map[event].pop(name) |
297 | 318 | ||
298 | if data: | 319 | if data is not None: |
299 | bbhands_mc = (data.getVar("__BBHANDLERS_MC") or []) | 320 | bbhands_mc = (data.getVar("__BBHANDLERS_MC") or set()) |
300 | if name in bbhands_mc: | 321 | if name in bbhands_mc: |
301 | bbhands_mc.remove(name) | 322 | bbhands_mc.remove(name) |
302 | data.setVar("__BBHANDLERS_MC", bbhands_mc) | 323 | data.setVar("__BBHANDLERS_MC", bbhands_mc) |
@@ -313,21 +334,23 @@ def set_eventfilter(func): | |||
313 | _eventfilter = func | 334 | _eventfilter = func |
314 | 335 | ||
315 | def register_UIHhandler(handler, mainui=False): | 336 | def register_UIHhandler(handler, mainui=False): |
316 | bb.event._ui_handler_seq = bb.event._ui_handler_seq + 1 | 337 | with bb.utils.lock_timeout(_thread_lock): |
317 | _ui_handlers[_ui_handler_seq] = handler | 338 | bb.event._ui_handler_seq = bb.event._ui_handler_seq + 1 |
318 | level, debug_domains = bb.msg.constructLogOptions() | 339 | _ui_handlers[_ui_handler_seq] = handler |
319 | _ui_logfilters[_ui_handler_seq] = UIEventFilter(level, debug_domains) | 340 | level, debug_domains = bb.msg.constructLogOptions() |
320 | if mainui: | 341 | _ui_logfilters[_ui_handler_seq] = UIEventFilter(level, debug_domains) |
321 | global _uiready | 342 | if mainui: |
322 | _uiready = _ui_handler_seq | 343 | global _uiready |
323 | return _ui_handler_seq | 344 | _uiready = _ui_handler_seq |
345 | return _ui_handler_seq | ||
324 | 346 | ||
325 | def unregister_UIHhandler(handlerNum, mainui=False): | 347 | def unregister_UIHhandler(handlerNum, mainui=False): |
326 | if mainui: | 348 | if mainui: |
327 | global _uiready | 349 | global _uiready |
328 | _uiready = False | 350 | _uiready = False |
329 | if handlerNum in _ui_handlers: | 351 | with bb.utils.lock_timeout(_thread_lock): |
330 | del _ui_handlers[handlerNum] | 352 | if handlerNum in _ui_handlers: |
353 | del _ui_handlers[handlerNum] | ||
331 | return | 354 | return |
332 | 355 | ||
333 | def get_uihandler(): | 356 | def get_uihandler(): |
@@ -408,6 +431,16 @@ class RecipeEvent(Event): | |||
408 | self.fn = fn | 431 | self.fn = fn |
409 | Event.__init__(self) | 432 | Event.__init__(self) |
410 | 433 | ||
434 | class RecipePreDeferredInherits(RecipeEvent): | ||
435 | """ | ||
436 | Called before deferred inherits are processed so code can snoop on class extensions for example | ||
437 | Limitations: It won't see inherits of inherited classes and the data is unexpanded | ||
438 | """ | ||
439 | def __init__(self, fn, inherits): | ||
440 | self.fn = fn | ||
441 | self.inherits = inherits | ||
442 | Event.__init__(self) | ||
443 | |||
411 | class RecipePreFinalise(RecipeEvent): | 444 | class RecipePreFinalise(RecipeEvent): |
412 | """ Recipe Parsing Complete but not yet finalised""" | 445 | """ Recipe Parsing Complete but not yet finalised""" |
413 | 446 | ||
@@ -482,7 +515,7 @@ class BuildCompleted(BuildBase, OperationCompleted): | |||
482 | BuildBase.__init__(self, n, p, failures) | 515 | BuildBase.__init__(self, n, p, failures) |
483 | 516 | ||
484 | class DiskFull(Event): | 517 | class DiskFull(Event): |
485 | """Disk full case build aborted""" | 518 | """Disk full case build halted""" |
486 | def __init__(self, dev, type, freespace, mountpoint): | 519 | def __init__(self, dev, type, freespace, mountpoint): |
487 | Event.__init__(self) | 520 | Event.__init__(self) |
488 | self._dev = dev | 521 | self._dev = dev |
@@ -666,6 +699,17 @@ class ReachableStamps(Event): | |||
666 | Event.__init__(self) | 699 | Event.__init__(self) |
667 | self.stamps = stamps | 700 | self.stamps = stamps |
668 | 701 | ||
702 | class StaleSetSceneTasks(Event): | ||
703 | """ | ||
704 | An event listing setscene tasks which are 'stale' and will | ||
705 | be rerun. The metadata may use to clean up stale data. | ||
706 | tasks is a mapping of tasks and matching stale stamps. | ||
707 | """ | ||
708 | |||
709 | def __init__(self, tasks): | ||
710 | Event.__init__(self) | ||
711 | self.tasks = tasks | ||
712 | |||
669 | class FilesMatchingFound(Event): | 713 | class FilesMatchingFound(Event): |
670 | """ | 714 | """ |
671 | Event when a list of files matching the supplied pattern has | 715 | Event when a list of files matching the supplied pattern has |
@@ -732,13 +776,7 @@ class LogHandler(logging.Handler): | |||
732 | 776 | ||
733 | def emit(self, record): | 777 | def emit(self, record): |
734 | if record.exc_info: | 778 | if record.exc_info: |
735 | etype, value, tb = record.exc_info | 779 | record.bb_exc_formatted = traceback.format_exception(*record.exc_info) |
736 | if hasattr(tb, 'tb_next'): | ||
737 | tb = list(bb.exceptions.extract_traceback(tb, context=3)) | ||
738 | # Need to turn the value into something the logging system can pickle | ||
739 | record.bb_exc_info = (etype, value, tb) | ||
740 | record.bb_exc_formatted = bb.exceptions.format_exception(etype, value, tb, limit=5) | ||
741 | value = str(value) | ||
742 | record.exc_info = None | 780 | record.exc_info = None |
743 | fire(record, None) | 781 | fire(record, None) |
744 | 782 | ||
@@ -749,7 +787,7 @@ class LogHandler(logging.Handler): | |||
749 | class MetadataEvent(Event): | 787 | class MetadataEvent(Event): |
750 | """ | 788 | """ |
751 | Generic event that target for OE-Core classes | 789 | Generic event that target for OE-Core classes |
752 | to report information during asynchrous execution | 790 | to report information during asynchronous execution |
753 | """ | 791 | """ |
754 | def __init__(self, eventtype, eventdata): | 792 | def __init__(self, eventtype, eventdata): |
755 | Event.__init__(self) | 793 | Event.__init__(self) |
@@ -830,3 +868,19 @@ class FindSigInfoResult(Event): | |||
830 | def __init__(self, result): | 868 | def __init__(self, result): |
831 | Event.__init__(self) | 869 | Event.__init__(self) |
832 | self.result = result | 870 | self.result = result |
871 | |||
872 | class GetTaskSignatureResult(Event): | ||
873 | """ | ||
874 | Event to return results from GetTaskSignatures command | ||
875 | """ | ||
876 | def __init__(self, sig): | ||
877 | Event.__init__(self) | ||
878 | self.sig = sig | ||
879 | |||
880 | class ParseError(Event): | ||
881 | """ | ||
882 | Event to indicate parse failed | ||
883 | """ | ||
884 | def __init__(self, msg): | ||
885 | super().__init__() | ||
886 | self._msg = msg | ||