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.py204
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
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(d):
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"""
@@ -232,26 +248,31 @@ noop = lambda _: None
232def register(name, handler, mask=None, filename=None, lineno=None, data=None): 248def 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
284def remove(name, handler, data=None): 305def 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
315def register_UIHhandler(handler, mainui=False): 336def 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
325def unregister_UIHhandler(handlerNum, mainui=False): 347def 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
333def get_uihandler(): 356def 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
434class 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
411class RecipePreFinalise(RecipeEvent): 444class 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
484class DiskFull(Event): 517class 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
702class 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
669class FilesMatchingFound(Event): 713class 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):
749class MetadataEvent(Event): 787class 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
872class 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
880class ParseError(Event):
881 """
882 Event to indicate parse failed
883 """
884 def __init__(self, msg):
885 super().__init__()
886 self._msg = msg