xref: /openbmc/openbmc/poky/bitbake/lib/bb/__init__.py (revision f376f21a8c3687f1d30191905f79aedd2171fe4a)
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