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.15.1" 13 14import sys 15if sys.version_info < (3, 9, 0): 16 raise RuntimeError("Sorry, python 3.9.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 40import multiprocessing as mp 41 42# Python 3.14 changes the default multiprocessing context from "fork" to 43# "forkserver". However, bitbake heavily relies on "fork" behavior to 44# efficiently pass data to the child processes. Places that need this should do: 45# from bb import multiprocessing 46# in place of 47# import multiprocessing 48 49class MultiprocessingContext(object): 50 """ 51 Multiprocessing proxy object that uses the "fork" context for a property if 52 available, otherwise goes to the main multiprocessing module. This allows 53 it to be a drop-in replacement for the multiprocessing module, but use the 54 fork context 55 """ 56 def __init__(self): 57 super().__setattr__("_ctx", mp.get_context("fork")) 58 59 def __getattr__(self, name): 60 if hasattr(self._ctx, name): 61 return getattr(self._ctx, name) 62 return getattr(mp, name) 63 64 def __setattr__(self, name, value): 65 raise AttributeError(f"Unable to set attribute {name}") 66 67multiprocessing = MultiprocessingContext() 68 69 70class NullHandler(logging.Handler): 71 def emit(self, record): 72 pass 73 74class BBLoggerMixin(object): 75 def __init__(self, *args, **kwargs): 76 # Does nothing to allow calling super() from derived classes 77 pass 78 79 def setup_bblogger(self, name): 80 if name.split(".")[0] == "BitBake": 81 self.debug = self._debug_helper 82 83 def _debug_helper(self, *args, **kwargs): 84 return self.bbdebug(1, *args, **kwargs) 85 86 def debug2(self, *args, **kwargs): 87 return self.bbdebug(2, *args, **kwargs) 88 89 def debug3(self, *args, **kwargs): 90 return self.bbdebug(3, *args, **kwargs) 91 92 def bbdebug(self, level, msg, *args, **kwargs): 93 loglevel = logging.DEBUG - level + 1 94 if not bb.event.worker_pid: 95 if self.name in bb.msg.loggerDefaultDomains and loglevel > (bb.msg.loggerDefaultDomains[self.name]): 96 return 97 if loglevel < bb.msg.loggerDefaultLogLevel: 98 return 99 100 if not isinstance(level, int) or not isinstance(msg, str): 101 mainlogger.warning("Invalid arguments in bbdebug: %s" % repr((level, msg,) + args)) 102 103 return self.log(loglevel, msg, *args, **kwargs) 104 105 def plain(self, msg, *args, **kwargs): 106 return self.log(logging.INFO + 1, msg, *args, **kwargs) 107 108 def verbose(self, msg, *args, **kwargs): 109 return self.log(logging.INFO - 1, msg, *args, **kwargs) 110 111 def verbnote(self, msg, *args, **kwargs): 112 return self.log(logging.INFO + 2, msg, *args, **kwargs) 113 114 def warnonce(self, msg, *args, **kwargs): 115 return self.log(logging.WARNING - 1, msg, *args, **kwargs) 116 117 def erroronce(self, msg, *args, **kwargs): 118 return self.log(logging.ERROR - 1, msg, *args, **kwargs) 119 120 121Logger = logging.getLoggerClass() 122class BBLogger(Logger, BBLoggerMixin): 123 def __init__(self, name, *args, **kwargs): 124 self.setup_bblogger(name) 125 super().__init__(name, *args, **kwargs) 126 127logging.raiseExceptions = False 128logging.setLoggerClass(BBLogger) 129 130class BBLoggerAdapter(logging.LoggerAdapter, BBLoggerMixin): 131 def __init__(self, logger, *args, **kwargs): 132 self.setup_bblogger(logger.name) 133 super().__init__(logger, *args, **kwargs) 134 135logging.LoggerAdapter = BBLoggerAdapter 136 137logger = logging.getLogger("BitBake") 138logger.addHandler(NullHandler()) 139logger.setLevel(logging.DEBUG - 2) 140 141mainlogger = logging.getLogger("BitBake.Main") 142 143class PrefixLoggerAdapter(logging.LoggerAdapter): 144 def __init__(self, prefix, logger): 145 super().__init__(logger, {}) 146 self.__msg_prefix = prefix 147 148 def process(self, msg, kwargs): 149 return "%s%s" %(self.__msg_prefix, msg), kwargs 150 151# This has to be imported after the setLoggerClass, as the import of bb.msg 152# can result in construction of the various loggers. 153import bb.msg 154 155from bb import fetch2 as fetch 156sys.modules['bb.fetch'] = sys.modules['bb.fetch2'] 157 158# Messaging convenience functions 159def plain(*args): 160 """ 161 Prints a message at "plain" level (higher level than a ``bb.note()``). 162 163 Arguments: 164 165 - ``args``: one or more strings to print. 166 """ 167 mainlogger.plain(''.join(args)) 168 169def debug(lvl, *args): 170 """ 171 Prints a debug message. 172 173 Arguments: 174 175 - ``lvl``: debug level. Higher value increases the debug level 176 (determined by ``bitbake -D``). 177 - ``args``: one or more strings to print. 178 """ 179 if isinstance(lvl, str): 180 mainlogger.warning("Passed invalid debug level '%s' to bb.debug", lvl) 181 args = (lvl,) + args 182 lvl = 1 183 mainlogger.bbdebug(lvl, ''.join(args)) 184 185def note(*args): 186 """ 187 Prints a message at "note" level. 188 189 Arguments: 190 191 - ``args``: one or more strings to print. 192 """ 193 mainlogger.info(''.join(args)) 194 195def verbnote(*args): 196 """ 197 A higher priority note which will show on the console but isn't a warning. 198 199 Use in contexts when something is happening the user should be aware of but 200 they probably did something to make it happen. 201 202 Arguments: 203 204 - ``args``: one or more strings to print. 205 """ 206 mainlogger.verbnote(''.join(args)) 207 208# 209# Warnings - things the user likely needs to pay attention to and fix 210# 211def warn(*args): 212 """ 213 Prints a warning message. 214 215 Arguments: 216 217 - ``args``: one or more strings to print. 218 """ 219 mainlogger.warning(''.join(args)) 220 221def warnonce(*args): 222 """ 223 Prints a warning message like ``bb.warn()``, but only prints the message 224 once. 225 226 Arguments: 227 228 - ``args``: one or more strings to print. 229 """ 230 mainlogger.warnonce(''.join(args)) 231 232def error(*args, **kwargs): 233 """ 234 Prints an error message. 235 236 Arguments: 237 238 - ``args``: one or more strings to print. 239 """ 240 mainlogger.error(''.join(args), extra=kwargs) 241 242def erroronce(*args): 243 """ 244 Prints an error message like ``bb.error()``, but only prints the message 245 once. 246 247 Arguments: 248 249 - ``args``: one or more strings to print. 250 """ 251 mainlogger.erroronce(''.join(args)) 252 253def fatal(*args, **kwargs): 254 """ 255 Prints an error message and stops the BitBake execution. 256 257 Arguments: 258 259 - ``args``: one or more strings to print. 260 """ 261 mainlogger.critical(''.join(args), extra=kwargs) 262 raise BBHandledException() 263 264def deprecated(func, name=None, advice=""): 265 """This is a decorator which can be used to mark functions 266 as deprecated. It will result in a warning being emitted 267 when the function is used.""" 268 import warnings 269 270 if advice: 271 advice = ": %s" % advice 272 if name is None: 273 name = func.__name__ 274 275 def newFunc(*args, **kwargs): 276 warnings.warn("Call to deprecated function %s%s." % (name, 277 advice), 278 category=DeprecationWarning, 279 stacklevel=2) 280 return func(*args, **kwargs) 281 newFunc.__name__ = func.__name__ 282 newFunc.__doc__ = func.__doc__ 283 newFunc.__dict__.update(func.__dict__) 284 return newFunc 285 286# For compatibility 287def deprecate_import(current, modulename, fromlist, renames = None): 288 """Import objects from one module into another, wrapping them with a DeprecationWarning""" 289 290 module = __import__(modulename, fromlist = fromlist) 291 for position, objname in enumerate(fromlist): 292 obj = getattr(module, objname) 293 newobj = deprecated(obj, "{0}.{1}".format(current, objname), 294 "Please use {0}.{1} instead".format(modulename, objname)) 295 if renames: 296 newname = renames[position] 297 else: 298 newname = objname 299 300 setattr(sys.modules[current], newname, newobj) 301 302TaskData = namedtuple("TaskData", [ 303 "pn", 304 "taskname", 305 "fn", 306 "deps", 307 "provides", 308 "taskhash", 309 "unihash", 310 "hashfn", 311 "taskhash_deps", 312]) 313