1# 2# BitBake Build System Python Library 3# 4# Copyright (C) 2003 Holger Schurig 5# Copyright (C) 2003, 2004 Chris Larson 6# 7# Based on Gentoo's portage.py. 8# 9# SPDX-License-Identifier: GPL-2.0-only 10# 11 12__version__ = "2.9.0" 13 14import sys 15if sys.version_info < (3, 8, 0): 16 raise RuntimeError("Sorry, python 3.8.0 or later is required for this version of bitbake") 17 18if sys.version_info < (3, 10, 0): 19 # With python 3.8 and 3.9, we see errors of "libgcc_s.so.1 must be installed for pthread_cancel to work" 20 # https://stackoverflow.com/questions/64797838/libgcc-s-so-1-must-be-installed-for-pthread-cancel-to-work 21 # https://bugs.ams1.psf.io/issue42888 22 # so ensure libgcc_s is loaded early on 23 import ctypes 24 libgcc_s = ctypes.CDLL('libgcc_s.so.1') 25 26class BBHandledException(Exception): 27 """ 28 The big dilemma for generic bitbake code is what information to give the user 29 when an exception occurs. Any exception inheriting this base exception class 30 has already provided information to the user via some 'fired' message type such as 31 an explicitly fired event using bb.fire, or a bb.error message. If bitbake 32 encounters an exception derived from this class, no backtrace or other information 33 will be given to the user, its assumed the earlier event provided the relevant information. 34 """ 35 pass 36 37import os 38import logging 39 40 41class NullHandler(logging.Handler): 42 def emit(self, record): 43 pass 44 45class BBLoggerMixin(object): 46 def __init__(self, *args, **kwargs): 47 # Does nothing to allow calling super() from derived classes 48 pass 49 50 def setup_bblogger(self, name): 51 if name.split(".")[0] == "BitBake": 52 self.debug = self._debug_helper 53 54 def _debug_helper(self, *args, **kwargs): 55 return self.bbdebug(1, *args, **kwargs) 56 57 def debug2(self, *args, **kwargs): 58 return self.bbdebug(2, *args, **kwargs) 59 60 def debug3(self, *args, **kwargs): 61 return self.bbdebug(3, *args, **kwargs) 62 63 def bbdebug(self, level, msg, *args, **kwargs): 64 loglevel = logging.DEBUG - level + 1 65 if not bb.event.worker_pid: 66 if self.name in bb.msg.loggerDefaultDomains and loglevel > (bb.msg.loggerDefaultDomains[self.name]): 67 return 68 if loglevel < bb.msg.loggerDefaultLogLevel: 69 return 70 71 if not isinstance(level, int) or not isinstance(msg, str): 72 mainlogger.warning("Invalid arguments in bbdebug: %s" % repr((level, msg,) + args)) 73 74 return self.log(loglevel, msg, *args, **kwargs) 75 76 def plain(self, msg, *args, **kwargs): 77 return self.log(logging.INFO + 1, msg, *args, **kwargs) 78 79 def verbose(self, msg, *args, **kwargs): 80 return self.log(logging.INFO - 1, msg, *args, **kwargs) 81 82 def verbnote(self, msg, *args, **kwargs): 83 return self.log(logging.INFO + 2, msg, *args, **kwargs) 84 85 def warnonce(self, msg, *args, **kwargs): 86 return self.log(logging.WARNING - 1, msg, *args, **kwargs) 87 88 def erroronce(self, msg, *args, **kwargs): 89 return self.log(logging.ERROR - 1, msg, *args, **kwargs) 90 91 92Logger = logging.getLoggerClass() 93class BBLogger(Logger, BBLoggerMixin): 94 def __init__(self, name, *args, **kwargs): 95 self.setup_bblogger(name) 96 super().__init__(name, *args, **kwargs) 97 98logging.raiseExceptions = False 99logging.setLoggerClass(BBLogger) 100 101class BBLoggerAdapter(logging.LoggerAdapter, BBLoggerMixin): 102 def __init__(self, logger, *args, **kwargs): 103 self.setup_bblogger(logger.name) 104 super().__init__(logger, *args, **kwargs) 105 106 if sys.version_info < (3, 6): 107 # These properties were added in Python 3.6. Add them in older versions 108 # for compatibility 109 @property 110 def manager(self): 111 return self.logger.manager 112 113 @manager.setter 114 def manager(self, value): 115 self.logger.manager = value 116 117 @property 118 def name(self): 119 return self.logger.name 120 121 def __repr__(self): 122 logger = self.logger 123 level = logger.getLevelName(logger.getEffectiveLevel()) 124 return '<%s %s (%s)>' % (self.__class__.__name__, logger.name, level) 125 126logging.LoggerAdapter = BBLoggerAdapter 127 128logger = logging.getLogger("BitBake") 129logger.addHandler(NullHandler()) 130logger.setLevel(logging.DEBUG - 2) 131 132mainlogger = logging.getLogger("BitBake.Main") 133 134class PrefixLoggerAdapter(logging.LoggerAdapter): 135 def __init__(self, prefix, logger): 136 super().__init__(logger, {}) 137 self.__msg_prefix = prefix 138 139 def process(self, msg, kwargs): 140 return "%s%s" %(self.__msg_prefix, msg), kwargs 141 142# This has to be imported after the setLoggerClass, as the import of bb.msg 143# can result in construction of the various loggers. 144import bb.msg 145 146from bb import fetch2 as fetch 147sys.modules['bb.fetch'] = sys.modules['bb.fetch2'] 148 149# Messaging convenience functions 150def plain(*args): 151 mainlogger.plain(''.join(args)) 152 153def debug(lvl, *args): 154 if isinstance(lvl, str): 155 mainlogger.warning("Passed invalid debug level '%s' to bb.debug", lvl) 156 args = (lvl,) + args 157 lvl = 1 158 mainlogger.bbdebug(lvl, ''.join(args)) 159 160def note(*args): 161 mainlogger.info(''.join(args)) 162 163# 164# A higher prioity note which will show on the console but isn't a warning 165# 166# Something is happening the user should be aware of but they probably did 167# something to make it happen 168# 169def verbnote(*args): 170 mainlogger.verbnote(''.join(args)) 171 172# 173# Warnings - things the user likely needs to pay attention to and fix 174# 175def warn(*args): 176 mainlogger.warning(''.join(args)) 177 178def warnonce(*args): 179 mainlogger.warnonce(''.join(args)) 180 181def error(*args, **kwargs): 182 mainlogger.error(''.join(args), extra=kwargs) 183 184def erroronce(*args): 185 mainlogger.erroronce(''.join(args)) 186 187def fatal(*args, **kwargs): 188 mainlogger.critical(''.join(args), extra=kwargs) 189 raise BBHandledException() 190 191def deprecated(func, name=None, advice=""): 192 """This is a decorator which can be used to mark functions 193 as deprecated. It will result in a warning being emitted 194 when the function is used.""" 195 import warnings 196 197 if advice: 198 advice = ": %s" % advice 199 if name is None: 200 name = func.__name__ 201 202 def newFunc(*args, **kwargs): 203 warnings.warn("Call to deprecated function %s%s." % (name, 204 advice), 205 category=DeprecationWarning, 206 stacklevel=2) 207 return func(*args, **kwargs) 208 newFunc.__name__ = func.__name__ 209 newFunc.__doc__ = func.__doc__ 210 newFunc.__dict__.update(func.__dict__) 211 return newFunc 212 213# For compatibility 214def deprecate_import(current, modulename, fromlist, renames = None): 215 """Import objects from one module into another, wrapping them with a DeprecationWarning""" 216 import sys 217 218 module = __import__(modulename, fromlist = fromlist) 219 for position, objname in enumerate(fromlist): 220 obj = getattr(module, objname) 221 newobj = deprecated(obj, "{0}.{1}".format(current, objname), 222 "Please use {0}.{1} instead".format(modulename, objname)) 223 if renames: 224 newname = renames[position] 225 else: 226 newname = objname 227 228 setattr(sys.modules[current], newname, newobj) 229 230