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.1" 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 39from collections import namedtuple 40 41 42class NullHandler(logging.Handler): 43 def emit(self, record): 44 pass 45 46class BBLoggerMixin(object): 47 def __init__(self, *args, **kwargs): 48 # Does nothing to allow calling super() from derived classes 49 pass 50 51 def setup_bblogger(self, name): 52 if name.split(".")[0] == "BitBake": 53 self.debug = self._debug_helper 54 55 def _debug_helper(self, *args, **kwargs): 56 return self.bbdebug(1, *args, **kwargs) 57 58 def debug2(self, *args, **kwargs): 59 return self.bbdebug(2, *args, **kwargs) 60 61 def debug3(self, *args, **kwargs): 62 return self.bbdebug(3, *args, **kwargs) 63 64 def bbdebug(self, level, msg, *args, **kwargs): 65 loglevel = logging.DEBUG - level + 1 66 if not bb.event.worker_pid: 67 if self.name in bb.msg.loggerDefaultDomains and loglevel > (bb.msg.loggerDefaultDomains[self.name]): 68 return 69 if loglevel < bb.msg.loggerDefaultLogLevel: 70 return 71 72 if not isinstance(level, int) or not isinstance(msg, str): 73 mainlogger.warning("Invalid arguments in bbdebug: %s" % repr((level, msg,) + args)) 74 75 return self.log(loglevel, msg, *args, **kwargs) 76 77 def plain(self, msg, *args, **kwargs): 78 return self.log(logging.INFO + 1, msg, *args, **kwargs) 79 80 def verbose(self, msg, *args, **kwargs): 81 return self.log(logging.INFO - 1, msg, *args, **kwargs) 82 83 def verbnote(self, msg, *args, **kwargs): 84 return self.log(logging.INFO + 2, msg, *args, **kwargs) 85 86 def warnonce(self, msg, *args, **kwargs): 87 return self.log(logging.WARNING - 1, msg, *args, **kwargs) 88 89 def erroronce(self, msg, *args, **kwargs): 90 return self.log(logging.ERROR - 1, msg, *args, **kwargs) 91 92 93Logger = logging.getLoggerClass() 94class BBLogger(Logger, BBLoggerMixin): 95 def __init__(self, name, *args, **kwargs): 96 self.setup_bblogger(name) 97 super().__init__(name, *args, **kwargs) 98 99logging.raiseExceptions = False 100logging.setLoggerClass(BBLogger) 101 102class BBLoggerAdapter(logging.LoggerAdapter, BBLoggerMixin): 103 def __init__(self, logger, *args, **kwargs): 104 self.setup_bblogger(logger.name) 105 super().__init__(logger, *args, **kwargs) 106 107logging.LoggerAdapter = BBLoggerAdapter 108 109logger = logging.getLogger("BitBake") 110logger.addHandler(NullHandler()) 111logger.setLevel(logging.DEBUG - 2) 112 113mainlogger = logging.getLogger("BitBake.Main") 114 115class PrefixLoggerAdapter(logging.LoggerAdapter): 116 def __init__(self, prefix, logger): 117 super().__init__(logger, {}) 118 self.__msg_prefix = prefix 119 120 def process(self, msg, kwargs): 121 return "%s%s" %(self.__msg_prefix, msg), kwargs 122 123# This has to be imported after the setLoggerClass, as the import of bb.msg 124# can result in construction of the various loggers. 125import bb.msg 126 127from bb import fetch2 as fetch 128sys.modules['bb.fetch'] = sys.modules['bb.fetch2'] 129 130# Messaging convenience functions 131def plain(*args): 132 mainlogger.plain(''.join(args)) 133 134def debug(lvl, *args): 135 if isinstance(lvl, str): 136 mainlogger.warning("Passed invalid debug level '%s' to bb.debug", lvl) 137 args = (lvl,) + args 138 lvl = 1 139 mainlogger.bbdebug(lvl, ''.join(args)) 140 141def note(*args): 142 mainlogger.info(''.join(args)) 143 144# 145# A higher prioity note which will show on the console but isn't a warning 146# 147# Something is happening the user should be aware of but they probably did 148# something to make it happen 149# 150def verbnote(*args): 151 mainlogger.verbnote(''.join(args)) 152 153# 154# Warnings - things the user likely needs to pay attention to and fix 155# 156def warn(*args): 157 mainlogger.warning(''.join(args)) 158 159def warnonce(*args): 160 mainlogger.warnonce(''.join(args)) 161 162def error(*args, **kwargs): 163 mainlogger.error(''.join(args), extra=kwargs) 164 165def erroronce(*args): 166 mainlogger.erroronce(''.join(args)) 167 168def fatal(*args, **kwargs): 169 mainlogger.critical(''.join(args), extra=kwargs) 170 raise BBHandledException() 171 172def deprecated(func, name=None, advice=""): 173 """This is a decorator which can be used to mark functions 174 as deprecated. It will result in a warning being emitted 175 when the function is used.""" 176 import warnings 177 178 if advice: 179 advice = ": %s" % advice 180 if name is None: 181 name = func.__name__ 182 183 def newFunc(*args, **kwargs): 184 warnings.warn("Call to deprecated function %s%s." % (name, 185 advice), 186 category=DeprecationWarning, 187 stacklevel=2) 188 return func(*args, **kwargs) 189 newFunc.__name__ = func.__name__ 190 newFunc.__doc__ = func.__doc__ 191 newFunc.__dict__.update(func.__dict__) 192 return newFunc 193 194# For compatibility 195def deprecate_import(current, modulename, fromlist, renames = None): 196 """Import objects from one module into another, wrapping them with a DeprecationWarning""" 197 import sys 198 199 module = __import__(modulename, fromlist = fromlist) 200 for position, objname in enumerate(fromlist): 201 obj = getattr(module, objname) 202 newobj = deprecated(obj, "{0}.{1}".format(current, objname), 203 "Please use {0}.{1} instead".format(modulename, objname)) 204 if renames: 205 newname = renames[position] 206 else: 207 newname = objname 208 209 setattr(sys.modules[current], newname, newobj) 210 211TaskData = namedtuple("TaskData", [ 212 "pn", 213 "taskname", 214 "fn", 215 "deps", 216 "provides", 217 "taskhash", 218 "unihash", 219 "hashfn", 220 "taskhash_deps", 221]) 222