event.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  1. # ex:ts=4:sw=4:sts=4:et
  2. # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
  3. """
  4. BitBake 'Event' implementation
  5. Classes and functions for manipulating 'events' in the
  6. BitBake build tools.
  7. """
  8. # Copyright (C) 2003, 2004 Chris Larson
  9. #
  10. # This program is free software; you can redistribute it and/or modify
  11. # it under the terms of the GNU General Public License version 2 as
  12. # published by the Free Software Foundation.
  13. #
  14. # This program is distributed in the hope that it will be useful,
  15. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. # GNU General Public License for more details.
  18. #
  19. # You should have received a copy of the GNU General Public License along
  20. # with this program; if not, write to the Free Software Foundation, Inc.,
  21. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  22. import os, sys
  23. import warnings
  24. import pickle
  25. import logging
  26. import atexit
  27. import traceback
  28. import ast
  29. import bb.utils
  30. import bb.compat
  31. import bb.exceptions
  32. # This is the pid for which we should generate the event. This is set when
  33. # the runqueue forks off.
  34. worker_pid = 0
  35. worker_fire = None
  36. logger = logging.getLogger('BitBake.Event')
  37. class Event(object):
  38. """Base class for events"""
  39. def __init__(self):
  40. self.pid = worker_pid
  41. Registered = 10
  42. AlreadyRegistered = 14
  43. def get_class_handlers():
  44. return _handlers
  45. def set_class_handlers(h):
  46. global _handlers
  47. _handlers = h
  48. def clean_class_handlers():
  49. return bb.compat.OrderedDict()
  50. # Internal
  51. _handlers = clean_class_handlers()
  52. _ui_handlers = {}
  53. _ui_logfilters = {}
  54. _ui_handler_seq = 0
  55. _event_handler_map = {}
  56. _catchall_handlers = {}
  57. _eventfilter = None
  58. _uiready = False
  59. if hasattr(__builtins__, '__setitem__'):
  60. builtins = __builtins__
  61. else:
  62. builtins = __builtins__.__dict__
  63. def execute_handler(name, handler, event, d):
  64. event.data = d
  65. addedd = False
  66. if 'd' not in builtins:
  67. builtins['d'] = d
  68. addedd = True
  69. try:
  70. ret = handler(event)
  71. except (bb.parse.SkipRecipe, bb.BBHandledException):
  72. raise
  73. except Exception:
  74. etype, value, tb = sys.exc_info()
  75. logger.error("Execution of event handler '%s' failed" % name,
  76. exc_info=(etype, value, tb.tb_next))
  77. raise
  78. except SystemExit as exc:
  79. if exc.code != 0:
  80. logger.error("Execution of event handler '%s' failed" % name)
  81. raise
  82. finally:
  83. del event.data
  84. if addedd:
  85. del builtins['d']
  86. def fire_class_handlers(event, d):
  87. if isinstance(event, logging.LogRecord):
  88. return
  89. eid = str(event.__class__)[8:-2]
  90. evt_hmap = _event_handler_map.get(eid, {})
  91. for name, handler in list(_handlers.items()):
  92. if name in _catchall_handlers or name in evt_hmap:
  93. if _eventfilter:
  94. if not _eventfilter(name, handler, event, d):
  95. continue
  96. execute_handler(name, handler, event, d)
  97. ui_queue = []
  98. @atexit.register
  99. def print_ui_queue():
  100. """If we're exiting before a UI has been spawned, display any queued
  101. LogRecords to the console."""
  102. logger = logging.getLogger("BitBake")
  103. if not _uiready:
  104. from bb.msg import BBLogFormatter
  105. console = logging.StreamHandler(sys.stdout)
  106. console.setFormatter(BBLogFormatter("%(levelname)s: %(message)s"))
  107. logger.handlers = [console]
  108. # First check to see if we have any proper messages
  109. msgprint = False
  110. for event in ui_queue:
  111. if isinstance(event, logging.LogRecord):
  112. if event.levelno > logging.DEBUG:
  113. logger.handle(event)
  114. msgprint = True
  115. if msgprint:
  116. return
  117. # Nope, so just print all of the messages we have (including debug messages)
  118. for event in ui_queue:
  119. if isinstance(event, logging.LogRecord):
  120. logger.handle(event)
  121. def fire_ui_handlers(event, d):
  122. if not _uiready:
  123. # No UI handlers registered yet, queue up the messages
  124. ui_queue.append(event)
  125. return
  126. errors = []
  127. for h in _ui_handlers:
  128. #print "Sending event %s" % event
  129. try:
  130. if not _ui_logfilters[h].filter(event):
  131. continue
  132. # We use pickle here since it better handles object instances
  133. # which xmlrpc's marshaller does not. Events *must* be serializable
  134. # by pickle.
  135. if hasattr(_ui_handlers[h].event, "sendpickle"):
  136. _ui_handlers[h].event.sendpickle((pickle.dumps(event)))
  137. else:
  138. _ui_handlers[h].event.send(event)
  139. except:
  140. errors.append(h)
  141. for h in errors:
  142. del _ui_handlers[h]
  143. def fire(event, d):
  144. """Fire off an Event"""
  145. # We can fire class handlers in the worker process context and this is
  146. # desired so they get the task based datastore.
  147. # UI handlers need to be fired in the server context so we defer this. They
  148. # don't have a datastore so the datastore context isn't a problem.
  149. fire_class_handlers(event, d)
  150. if worker_fire:
  151. worker_fire(event, d)
  152. else:
  153. fire_ui_handlers(event, d)
  154. def fire_from_worker(event, d):
  155. fire_ui_handlers(event, d)
  156. noop = lambda _: None
  157. def register(name, handler, mask=None, filename=None, lineno=None):
  158. """Register an Event handler"""
  159. # already registered
  160. if name in _handlers:
  161. return AlreadyRegistered
  162. if handler is not None:
  163. # handle string containing python code
  164. if isinstance(handler, str):
  165. tmp = "def %s(e):\n%s" % (name, handler)
  166. try:
  167. code = bb.methodpool.compile_cache(tmp)
  168. if not code:
  169. if filename is None:
  170. filename = "%s(e)" % name
  171. code = compile(tmp, filename, "exec", ast.PyCF_ONLY_AST)
  172. if lineno is not None:
  173. ast.increment_lineno(code, lineno-1)
  174. code = compile(code, filename, "exec")
  175. bb.methodpool.compile_cache_add(tmp, code)
  176. except SyntaxError:
  177. logger.error("Unable to register event handler '%s':\n%s", name,
  178. ''.join(traceback.format_exc(limit=0)))
  179. _handlers[name] = noop
  180. return
  181. env = {}
  182. bb.utils.better_exec(code, env)
  183. func = bb.utils.better_eval(name, env)
  184. _handlers[name] = func
  185. else:
  186. _handlers[name] = handler
  187. if not mask or '*' in mask:
  188. _catchall_handlers[name] = True
  189. else:
  190. for m in mask:
  191. if _event_handler_map.get(m, None) is None:
  192. _event_handler_map[m] = {}
  193. _event_handler_map[m][name] = True
  194. return Registered
  195. def remove(name, handler):
  196. """Remove an Event handler"""
  197. _handlers.pop(name)
  198. def set_eventfilter(func):
  199. global _eventfilter
  200. _eventfilter = func
  201. def register_UIHhandler(handler, mainui=False):
  202. if mainui:
  203. global _uiready
  204. _uiready = True
  205. bb.event._ui_handler_seq = bb.event._ui_handler_seq + 1
  206. _ui_handlers[_ui_handler_seq] = handler
  207. level, debug_domains = bb.msg.constructLogOptions()
  208. _ui_logfilters[_ui_handler_seq] = UIEventFilter(level, debug_domains)
  209. return _ui_handler_seq
  210. def unregister_UIHhandler(handlerNum):
  211. if handlerNum in _ui_handlers:
  212. del _ui_handlers[handlerNum]
  213. return
  214. # Class to allow filtering of events and specific filtering of LogRecords *before* we put them over the IPC
  215. class UIEventFilter(object):
  216. def __init__(self, level, debug_domains):
  217. self.update(None, level, debug_domains)
  218. def update(self, eventmask, level, debug_domains):
  219. self.eventmask = eventmask
  220. self.stdlevel = level
  221. self.debug_domains = debug_domains
  222. def filter(self, event):
  223. if isinstance(event, logging.LogRecord):
  224. if event.levelno >= self.stdlevel:
  225. return True
  226. if event.name in self.debug_domains and event.levelno >= self.debug_domains[event.name]:
  227. return True
  228. return False
  229. eid = str(event.__class__)[8:-2]
  230. if self.eventmask and eid not in self.eventmask:
  231. return False
  232. return True
  233. def set_UIHmask(handlerNum, level, debug_domains, mask):
  234. if not handlerNum in _ui_handlers:
  235. return False
  236. if '*' in mask:
  237. _ui_logfilters[handlerNum].update(None, level, debug_domains)
  238. else:
  239. _ui_logfilters[handlerNum].update(mask, level, debug_domains)
  240. return True
  241. def getName(e):
  242. """Returns the name of a class or class instance"""
  243. if getattr(e, "__name__", None) == None:
  244. return e.__class__.__name__
  245. else:
  246. return e.__name__
  247. class OperationStarted(Event):
  248. """An operation has begun"""
  249. def __init__(self, msg = "Operation Started"):
  250. Event.__init__(self)
  251. self.msg = msg
  252. class OperationCompleted(Event):
  253. """An operation has completed"""
  254. def __init__(self, total, msg = "Operation Completed"):
  255. Event.__init__(self)
  256. self.total = total
  257. self.msg = msg
  258. class OperationProgress(Event):
  259. """An operation is in progress"""
  260. def __init__(self, current, total, msg = "Operation in Progress"):
  261. Event.__init__(self)
  262. self.current = current
  263. self.total = total
  264. self.msg = msg + ": %s/%s" % (current, total);
  265. class ConfigParsed(Event):
  266. """Configuration Parsing Complete"""
  267. class RecipeEvent(Event):
  268. def __init__(self, fn):
  269. self.fn = fn
  270. Event.__init__(self)
  271. class RecipePreFinalise(RecipeEvent):
  272. """ Recipe Parsing Complete but not yet finialised"""
  273. class RecipeParsed(RecipeEvent):
  274. """ Recipe Parsing Complete """
  275. class StampUpdate(Event):
  276. """Trigger for any adjustment of the stamp files to happen"""
  277. def __init__(self, targets, stampfns):
  278. self._targets = targets
  279. self._stampfns = stampfns
  280. Event.__init__(self)
  281. def getStampPrefix(self):
  282. return self._stampfns
  283. def getTargets(self):
  284. return self._targets
  285. stampPrefix = property(getStampPrefix)
  286. targets = property(getTargets)
  287. class BuildBase(Event):
  288. """Base class for bbmake run events"""
  289. def __init__(self, n, p, failures = 0):
  290. self._name = n
  291. self._pkgs = p
  292. Event.__init__(self)
  293. self._failures = failures
  294. def getPkgs(self):
  295. return self._pkgs
  296. def setPkgs(self, pkgs):
  297. self._pkgs = pkgs
  298. def getName(self):
  299. return self._name
  300. def setName(self, name):
  301. self._name = name
  302. def getCfg(self):
  303. return self.data
  304. def setCfg(self, cfg):
  305. self.data = cfg
  306. def getFailures(self):
  307. """
  308. Return the number of failed packages
  309. """
  310. return self._failures
  311. pkgs = property(getPkgs, setPkgs, None, "pkgs property")
  312. name = property(getName, setName, None, "name property")
  313. cfg = property(getCfg, setCfg, None, "cfg property")
  314. class BuildStarted(BuildBase, OperationStarted):
  315. """bbmake build run started"""
  316. def __init__(self, n, p, failures = 0):
  317. OperationStarted.__init__(self, "Building Started")
  318. BuildBase.__init__(self, n, p, failures)
  319. class BuildCompleted(BuildBase, OperationCompleted):
  320. """bbmake build run completed"""
  321. def __init__(self, total, n, p, failures=0, interrupted=0):
  322. if not failures:
  323. OperationCompleted.__init__(self, total, "Building Succeeded")
  324. else:
  325. OperationCompleted.__init__(self, total, "Building Failed")
  326. self._interrupted = interrupted
  327. BuildBase.__init__(self, n, p, failures)
  328. class DiskFull(Event):
  329. """Disk full case build aborted"""
  330. def __init__(self, dev, type, freespace, mountpoint):
  331. Event.__init__(self)
  332. self._dev = dev
  333. self._type = type
  334. self._free = freespace
  335. self._mountpoint = mountpoint
  336. class NoProvider(Event):
  337. """No Provider for an Event"""
  338. def __init__(self, item, runtime=False, dependees=None, reasons=None, close_matches=None):
  339. Event.__init__(self)
  340. self._item = item
  341. self._runtime = runtime
  342. self._dependees = dependees
  343. self._reasons = reasons
  344. self._close_matches = close_matches
  345. def getItem(self):
  346. return self._item
  347. def isRuntime(self):
  348. return self._runtime
  349. class MultipleProviders(Event):
  350. """Multiple Providers"""
  351. def __init__(self, item, candidates, runtime = False):
  352. Event.__init__(self)
  353. self._item = item
  354. self._candidates = candidates
  355. self._is_runtime = runtime
  356. def isRuntime(self):
  357. """
  358. Is this a runtime issue?
  359. """
  360. return self._is_runtime
  361. def getItem(self):
  362. """
  363. The name for the to be build item
  364. """
  365. return self._item
  366. def getCandidates(self):
  367. """
  368. Get the possible Candidates for a PROVIDER.
  369. """
  370. return self._candidates
  371. class ParseStarted(OperationStarted):
  372. """Recipe parsing for the runqueue has begun"""
  373. def __init__(self, total):
  374. OperationStarted.__init__(self, "Recipe parsing Started")
  375. self.total = total
  376. class ParseCompleted(OperationCompleted):
  377. """Recipe parsing for the runqueue has completed"""
  378. def __init__(self, cached, parsed, skipped, masked, virtuals, errors, total):
  379. OperationCompleted.__init__(self, total, "Recipe parsing Completed")
  380. self.cached = cached
  381. self.parsed = parsed
  382. self.skipped = skipped
  383. self.virtuals = virtuals
  384. self.masked = masked
  385. self.errors = errors
  386. self.sofar = cached + parsed
  387. class ParseProgress(OperationProgress):
  388. """Recipe parsing progress"""
  389. def __init__(self, current, total):
  390. OperationProgress.__init__(self, current, total, "Recipe parsing")
  391. class CacheLoadStarted(OperationStarted):
  392. """Loading of the dependency cache has begun"""
  393. def __init__(self, total):
  394. OperationStarted.__init__(self, "Loading cache Started")
  395. self.total = total
  396. class CacheLoadProgress(OperationProgress):
  397. """Cache loading progress"""
  398. def __init__(self, current, total):
  399. OperationProgress.__init__(self, current, total, "Loading cache")
  400. class CacheLoadCompleted(OperationCompleted):
  401. """Cache loading is complete"""
  402. def __init__(self, total, num_entries):
  403. OperationCompleted.__init__(self, total, "Loading cache Completed")
  404. self.num_entries = num_entries
  405. class TreeDataPreparationStarted(OperationStarted):
  406. """Tree data preparation started"""
  407. def __init__(self):
  408. OperationStarted.__init__(self, "Preparing tree data Started")
  409. class TreeDataPreparationProgress(OperationProgress):
  410. """Tree data preparation is in progress"""
  411. def __init__(self, current, total):
  412. OperationProgress.__init__(self, current, total, "Preparing tree data")
  413. class TreeDataPreparationCompleted(OperationCompleted):
  414. """Tree data preparation completed"""
  415. def __init__(self, total):
  416. OperationCompleted.__init__(self, total, "Preparing tree data Completed")
  417. class DepTreeGenerated(Event):
  418. """
  419. Event when a dependency tree has been generated
  420. """
  421. def __init__(self, depgraph):
  422. Event.__init__(self)
  423. self._depgraph = depgraph
  424. class TargetsTreeGenerated(Event):
  425. """
  426. Event when a set of buildable targets has been generated
  427. """
  428. def __init__(self, model):
  429. Event.__init__(self)
  430. self._model = model
  431. class ReachableStamps(Event):
  432. """
  433. An event listing all stamps reachable after parsing
  434. which the metadata may use to clean up stale data
  435. """
  436. def __init__(self, stamps):
  437. Event.__init__(self)
  438. self.stamps = stamps
  439. class FilesMatchingFound(Event):
  440. """
  441. Event when a list of files matching the supplied pattern has
  442. been generated
  443. """
  444. def __init__(self, pattern, matches):
  445. Event.__init__(self)
  446. self._pattern = pattern
  447. self._matches = matches
  448. class CoreBaseFilesFound(Event):
  449. """
  450. Event when a list of appropriate config files has been generated
  451. """
  452. def __init__(self, paths):
  453. Event.__init__(self)
  454. self._paths = paths
  455. class ConfigFilesFound(Event):
  456. """
  457. Event when a list of appropriate config files has been generated
  458. """
  459. def __init__(self, variable, values):
  460. Event.__init__(self)
  461. self._variable = variable
  462. self._values = values
  463. class ConfigFilePathFound(Event):
  464. """
  465. Event when a path for a config file has been found
  466. """
  467. def __init__(self, path):
  468. Event.__init__(self)
  469. self._path = path
  470. class MsgBase(Event):
  471. """Base class for messages"""
  472. def __init__(self, msg):
  473. self._message = msg
  474. Event.__init__(self)
  475. class MsgDebug(MsgBase):
  476. """Debug Message"""
  477. class MsgNote(MsgBase):
  478. """Note Message"""
  479. class MsgWarn(MsgBase):
  480. """Warning Message"""
  481. class MsgError(MsgBase):
  482. """Error Message"""
  483. class MsgFatal(MsgBase):
  484. """Fatal Message"""
  485. class MsgPlain(MsgBase):
  486. """General output"""
  487. class LogExecTTY(Event):
  488. """Send event containing program to spawn on tty of the logger"""
  489. def __init__(self, msg, prog, sleep_delay, retries):
  490. Event.__init__(self)
  491. self.msg = msg
  492. self.prog = prog
  493. self.sleep_delay = sleep_delay
  494. self.retries = retries
  495. class LogHandler(logging.Handler):
  496. """Dispatch logging messages as bitbake events"""
  497. def emit(self, record):
  498. if record.exc_info:
  499. etype, value, tb = record.exc_info
  500. if hasattr(tb, 'tb_next'):
  501. tb = list(bb.exceptions.extract_traceback(tb, context=3))
  502. # Need to turn the value into something the logging system can pickle
  503. record.bb_exc_info = (etype, value, tb)
  504. record.bb_exc_formatted = bb.exceptions.format_exception(etype, value, tb, limit=5)
  505. value = str(value)
  506. record.exc_info = None
  507. fire(record, None)
  508. def filter(self, record):
  509. record.taskpid = worker_pid
  510. return True
  511. class RequestPackageInfo(Event):
  512. """
  513. Event to request package information
  514. """
  515. class PackageInfo(Event):
  516. """
  517. Package information for GUI
  518. """
  519. def __init__(self, pkginfolist):
  520. Event.__init__(self)
  521. self._pkginfolist = pkginfolist
  522. class MetadataEvent(Event):
  523. """
  524. Generic event that target for OE-Core classes
  525. to report information during asynchrous execution
  526. """
  527. def __init__(self, eventtype, eventdata):
  528. Event.__init__(self)
  529. self.type = eventtype
  530. self._localdata = eventdata
  531. class SanityCheck(Event):
  532. """
  533. Event to run sanity checks, either raise errors or generate events as return status.
  534. """
  535. def __init__(self, generateevents = True):
  536. Event.__init__(self)
  537. self.generateevents = generateevents
  538. class SanityCheckPassed(Event):
  539. """
  540. Event to indicate sanity check has passed
  541. """
  542. class SanityCheckFailed(Event):
  543. """
  544. Event to indicate sanity check has failed
  545. """
  546. def __init__(self, msg, network_error=False):
  547. Event.__init__(self)
  548. self._msg = msg
  549. self._network_error = network_error
  550. class NetworkTest(Event):
  551. """
  552. Event to run network connectivity tests, either raise errors or generate events as return status.
  553. """
  554. def __init__(self, generateevents = True):
  555. Event.__init__(self)
  556. self.generateevents = generateevents
  557. class NetworkTestPassed(Event):
  558. """
  559. Event to indicate network test has passed
  560. """
  561. class NetworkTestFailed(Event):
  562. """
  563. Event to indicate network test has failed
  564. """