summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/bb/event.py
diff options
context:
space:
mode:
Diffstat (limited to 'bitbake/lib/bb/event.py')
-rw-r--r--bitbake/lib/bb/event.py211
1 files changed, 134 insertions, 77 deletions
diff --git a/bitbake/lib/bb/event.py b/bitbake/lib/bb/event.py
index 23e1f3187b..ddf1006c29 100644
--- a/bitbake/lib/bb/event.py
+++ b/bitbake/lib/bb/event.py
@@ -19,7 +19,6 @@ import sys
19import threading 19import threading
20import traceback 20import traceback
21 21
22import bb.exceptions
23import bb.utils 22import 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()
73if hasattr(__builtins__, '__setitem__'):
74 builtins = __builtins__
75else:
76 builtins = __builtins__.__dict__
77 72
78def enable_threadlock(): 73def enable_threadlock():
79 global _thread_lock_enabled 74 # Always needed now
80 _thread_lock_enabled = True 75 return
81 76
82def disable_threadlock(): 77def disable_threadlock():
83 global _thread_lock_enabled 78 # Always needed now
84 _thread_lock_enabled = False 79 return
80
81def enable_heartbeat():
82 global _heartbeat_enabled
83 _heartbeat_enabled = True
84
85def 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#
93def check_for_interrupts():
94 global _should_exit
95 if _should_exit.is_set():
96 bb.warn("Exiting due to interrupt.")
97 raise bb.BBHandledException()
85 98
86def execute_handler(name, handler, event, d): 99def 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
110def fire_class_handlers(event, d): 118def 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
175def fire_ui_handlers(event, d): 189def 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
208def fire(event, d): 224def fire(event, d):
209 """Fire off an Event""" 225 """Fire off an Event"""
@@ -220,9 +236,12 @@ def fire(event, d):
220 # If messages have been queued up, clear the queue 236 # If messages have been queued up, clear the queue
221 global _uiready, ui_queue 237 global _uiready, ui_queue
222 if _uiready and ui_queue: 238 if _uiready and ui_queue:
223 for queue_event in ui_queue: 239 with bb.utils.lock_timeout_nocheck(_thread_lock):
240 queue = ui_queue
241 ui_queue = []
242 for queue_event in queue:
224 fire_ui_handlers(queue_event, d) 243 fire_ui_handlers(queue_event, d)
225 ui_queue = [] 244
226 fire_ui_handlers(event, d) 245 fire_ui_handlers(event, d)
227 246
228def fire_from_worker(event, d): 247def fire_from_worker(event, d):
@@ -232,26 +251,31 @@ noop = lambda _: None
232def register(name, handler, mask=None, filename=None, lineno=None, data=None): 251def register(name, handler, mask=None, filename=None, lineno=None, data=None):
233 """Register an Event handler""" 252 """Register an Event handler"""
234 253
235 if data and data.getVar("BB_CURRENT_MC"): 254 if data is not None and data.getVar("BB_CURRENT_MC"):
236 mc = data.getVar("BB_CURRENT_MC") 255 mc = data.getVar("BB_CURRENT_MC")
237 name = '%s%s' % (mc.replace('-', '_'), name) 256 name = '%s%s' % (mc.replace('-', '_'), name)
238 257
239 # already registered 258 # already registered
240 if name in _handlers: 259 if name in _handlers:
260 if data is not None:
261 bbhands_mc = (data.getVar("__BBHANDLERS_MC") or set())
262 bbhands_mc.add(name)
263 data.setVar("__BBHANDLERS_MC", bbhands_mc)
241 return AlreadyRegistered 264 return AlreadyRegistered
242 265
243 if handler is not None: 266 if handler is not None:
244 # handle string containing python code 267 # handle string containing python code
245 if isinstance(handler, str): 268 if isinstance(handler, str):
246 tmp = "def %s(e):\n%s" % (name, handler) 269 tmp = "def %s(e, d):\n%s" % (name, handler)
270 # Inject empty lines to make code match lineno in filename
271 if lineno is not None:
272 tmp = "\n" * (lineno-1) + tmp
247 try: 273 try:
248 code = bb.methodpool.compile_cache(tmp) 274 code = bb.methodpool.compile_cache(tmp)
249 if not code: 275 if not code:
250 if filename is None: 276 if filename is None:
251 filename = "%s(e)" % name 277 filename = "%s(e, d)" % name
252 code = compile(tmp, filename, "exec", ast.PyCF_ONLY_AST) 278 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") 279 code = compile(code, filename, "exec")
256 bb.methodpool.compile_cache_add(tmp, code) 280 bb.methodpool.compile_cache_add(tmp, code)
257 except SyntaxError: 281 except SyntaxError:
@@ -274,16 +298,16 @@ def register(name, handler, mask=None, filename=None, lineno=None, data=None):
274 _event_handler_map[m] = {} 298 _event_handler_map[m] = {}
275 _event_handler_map[m][name] = True 299 _event_handler_map[m][name] = True
276 300
277 if data: 301 if data is not None:
278 bbhands_mc = (data.getVar("__BBHANDLERS_MC") or []) 302 bbhands_mc = (data.getVar("__BBHANDLERS_MC") or set())
279 bbhands_mc.append(name) 303 bbhands_mc.add(name)
280 data.setVar("__BBHANDLERS_MC", bbhands_mc) 304 data.setVar("__BBHANDLERS_MC", bbhands_mc)
281 305
282 return Registered 306 return Registered
283 307
284def remove(name, handler, data=None): 308def remove(name, handler, data=None):
285 """Remove an Event handler""" 309 """Remove an Event handler"""
286 if data: 310 if data is not None:
287 if data.getVar("BB_CURRENT_MC"): 311 if data.getVar("BB_CURRENT_MC"):
288 mc = data.getVar("BB_CURRENT_MC") 312 mc = data.getVar("BB_CURRENT_MC")
289 name = '%s%s' % (mc.replace('-', '_'), name) 313 name = '%s%s' % (mc.replace('-', '_'), name)
@@ -295,8 +319,8 @@ def remove(name, handler, data=None):
295 if name in _event_handler_map[event]: 319 if name in _event_handler_map[event]:
296 _event_handler_map[event].pop(name) 320 _event_handler_map[event].pop(name)
297 321
298 if data: 322 if data is not None:
299 bbhands_mc = (data.getVar("__BBHANDLERS_MC") or []) 323 bbhands_mc = (data.getVar("__BBHANDLERS_MC") or set())
300 if name in bbhands_mc: 324 if name in bbhands_mc:
301 bbhands_mc.remove(name) 325 bbhands_mc.remove(name)
302 data.setVar("__BBHANDLERS_MC", bbhands_mc) 326 data.setVar("__BBHANDLERS_MC", bbhands_mc)
@@ -313,21 +337,23 @@ def set_eventfilter(func):
313 _eventfilter = func 337 _eventfilter = func
314 338
315def register_UIHhandler(handler, mainui=False): 339def register_UIHhandler(handler, mainui=False):
316 bb.event._ui_handler_seq = bb.event._ui_handler_seq + 1 340 with bb.utils.lock_timeout(_thread_lock):
317 _ui_handlers[_ui_handler_seq] = handler 341 bb.event._ui_handler_seq = bb.event._ui_handler_seq + 1
318 level, debug_domains = bb.msg.constructLogOptions() 342 _ui_handlers[_ui_handler_seq] = handler
319 _ui_logfilters[_ui_handler_seq] = UIEventFilter(level, debug_domains) 343 level, debug_domains = bb.msg.constructLogOptions()
320 if mainui: 344 _ui_logfilters[_ui_handler_seq] = UIEventFilter(level, debug_domains)
321 global _uiready 345 if mainui:
322 _uiready = _ui_handler_seq 346 global _uiready
323 return _ui_handler_seq 347 _uiready = _ui_handler_seq
348 return _ui_handler_seq
324 349
325def unregister_UIHhandler(handlerNum, mainui=False): 350def unregister_UIHhandler(handlerNum, mainui=False):
326 if mainui: 351 if mainui:
327 global _uiready 352 global _uiready
328 _uiready = False 353 _uiready = False
329 if handlerNum in _ui_handlers: 354 with bb.utils.lock_timeout(_thread_lock):
330 del _ui_handlers[handlerNum] 355 if handlerNum in _ui_handlers:
356 del _ui_handlers[handlerNum]
331 return 357 return
332 358
333def get_uihandler(): 359def get_uihandler():
@@ -408,6 +434,16 @@ class RecipeEvent(Event):
408 self.fn = fn 434 self.fn = fn
409 Event.__init__(self) 435 Event.__init__(self)
410 436
437class RecipePreDeferredInherits(RecipeEvent):
438 """
439 Called before deferred inherits are processed so code can snoop on class extensions for example
440 Limitations: It won't see inherits of inherited classes and the data is unexpanded
441 """
442 def __init__(self, fn, inherits):
443 self.fn = fn
444 self.inherits = inherits
445 Event.__init__(self)
446
411class RecipePreFinalise(RecipeEvent): 447class RecipePreFinalise(RecipeEvent):
412 """ Recipe Parsing Complete but not yet finalised""" 448 """ Recipe Parsing Complete but not yet finalised"""
413 449
@@ -482,7 +518,7 @@ class BuildCompleted(BuildBase, OperationCompleted):
482 BuildBase.__init__(self, n, p, failures) 518 BuildBase.__init__(self, n, p, failures)
483 519
484class DiskFull(Event): 520class DiskFull(Event):
485 """Disk full case build aborted""" 521 """Disk full case build halted"""
486 def __init__(self, dev, type, freespace, mountpoint): 522 def __init__(self, dev, type, freespace, mountpoint):
487 Event.__init__(self) 523 Event.__init__(self)
488 self._dev = dev 524 self._dev = dev
@@ -666,6 +702,17 @@ class ReachableStamps(Event):
666 Event.__init__(self) 702 Event.__init__(self)
667 self.stamps = stamps 703 self.stamps = stamps
668 704
705class StaleSetSceneTasks(Event):
706 """
707 An event listing setscene tasks which are 'stale' and will
708 be rerun. The metadata may use to clean up stale data.
709 tasks is a mapping of tasks and matching stale stamps.
710 """
711
712 def __init__(self, tasks):
713 Event.__init__(self)
714 self.tasks = tasks
715
669class FilesMatchingFound(Event): 716class FilesMatchingFound(Event):
670 """ 717 """
671 Event when a list of files matching the supplied pattern has 718 Event when a list of files matching the supplied pattern has
@@ -732,13 +779,7 @@ class LogHandler(logging.Handler):
732 779
733 def emit(self, record): 780 def emit(self, record):
734 if record.exc_info: 781 if record.exc_info:
735 etype, value, tb = record.exc_info 782 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 783 record.exc_info = None
743 fire(record, None) 784 fire(record, None)
744 785
@@ -749,7 +790,7 @@ class LogHandler(logging.Handler):
749class MetadataEvent(Event): 790class MetadataEvent(Event):
750 """ 791 """
751 Generic event that target for OE-Core classes 792 Generic event that target for OE-Core classes
752 to report information during asynchrous execution 793 to report information during asynchronous execution
753 """ 794 """
754 def __init__(self, eventtype, eventdata): 795 def __init__(self, eventtype, eventdata):
755 Event.__init__(self) 796 Event.__init__(self)
@@ -830,3 +871,19 @@ class FindSigInfoResult(Event):
830 def __init__(self, result): 871 def __init__(self, result):
831 Event.__init__(self) 872 Event.__init__(self)
832 self.result = result 873 self.result = result
874
875class GetTaskSignatureResult(Event):
876 """
877 Event to return results from GetTaskSignatures command
878 """
879 def __init__(self, sig):
880 Event.__init__(self)
881 self.sig = sig
882
883class ParseError(Event):
884 """
885 Event to indicate parse failed
886 """
887 def __init__(self, msg):
888 super().__init__()
889 self._msg = msg