xref: /openbmc/openbmc/poky/bitbake/lib/bb/__init__.py (revision c9537f57ab488bf5d90132917b0184e2527970a5)
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.12.0"
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
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    """
133    Prints a message at "plain" level (higher level than a ``bb.note()``).
134
135    Arguments:
136
137    -  ``args``: one or more strings to print.
138    """
139    mainlogger.plain(''.join(args))
140
141def debug(lvl, *args):
142    """
143    Prints a debug message.
144
145    Arguments:
146
147    -  ``lvl``: debug level. Higher value increases the debug level
148       (determined by ``bitbake -D``).
149    -  ``args``: one or more strings to print.
150    """
151    if isinstance(lvl, str):
152        mainlogger.warning("Passed invalid debug level '%s' to bb.debug", lvl)
153        args = (lvl,) + args
154        lvl = 1
155    mainlogger.bbdebug(lvl, ''.join(args))
156
157def note(*args):
158    """
159    Prints a message at "note" level.
160
161    Arguments:
162
163    -  ``args``: one or more strings to print.
164    """
165    mainlogger.info(''.join(args))
166
167def verbnote(*args):
168    """
169    A higher priority note which will show on the console but isn't a warning.
170
171    Use in contexts when something is happening the user should be aware of but
172    they probably did something to make it happen.
173
174    Arguments:
175
176    -  ``args``: one or more strings to print.
177    """
178    mainlogger.verbnote(''.join(args))
179
180#
181# Warnings - things the user likely needs to pay attention to and fix
182#
183def warn(*args):
184    """
185    Prints a warning message.
186
187    Arguments:
188
189    -  ``args``: one or more strings to print.
190    """
191    mainlogger.warning(''.join(args))
192
193def warnonce(*args):
194    """
195    Prints a warning message like ``bb.warn()``, but only prints the message
196    once.
197
198    Arguments:
199
200    -  ``args``: one or more strings to print.
201    """
202    mainlogger.warnonce(''.join(args))
203
204def error(*args, **kwargs):
205    """
206    Prints an error message.
207
208    Arguments:
209
210    -  ``args``: one or more strings to print.
211    """
212    mainlogger.error(''.join(args), extra=kwargs)
213
214def erroronce(*args):
215    """
216    Prints an error message like ``bb.error()``, but only prints the message
217    once.
218
219    Arguments:
220
221    -  ``args``: one or more strings to print.
222    """
223    mainlogger.erroronce(''.join(args))
224
225def fatal(*args, **kwargs):
226    """
227    Prints an error message and stops the BitBake execution.
228
229    Arguments:
230
231    -  ``args``: one or more strings to print.
232    """
233    mainlogger.critical(''.join(args), extra=kwargs)
234    raise BBHandledException()
235
236def deprecated(func, name=None, advice=""):
237    """This is a decorator which can be used to mark functions
238    as deprecated. It will result in a warning being emitted
239    when the function is used."""
240    import warnings
241
242    if advice:
243        advice = ": %s" % advice
244    if name is None:
245        name = func.__name__
246
247    def newFunc(*args, **kwargs):
248        warnings.warn("Call to deprecated function %s%s." % (name,
249                                                             advice),
250                      category=DeprecationWarning,
251                      stacklevel=2)
252        return func(*args, **kwargs)
253    newFunc.__name__ = func.__name__
254    newFunc.__doc__ = func.__doc__
255    newFunc.__dict__.update(func.__dict__)
256    return newFunc
257
258# For compatibility
259def deprecate_import(current, modulename, fromlist, renames = None):
260    """Import objects from one module into another, wrapping them with a DeprecationWarning"""
261
262    module = __import__(modulename, fromlist = fromlist)
263    for position, objname in enumerate(fromlist):
264        obj = getattr(module, objname)
265        newobj = deprecated(obj, "{0}.{1}".format(current, objname),
266                            "Please use {0}.{1} instead".format(modulename, objname))
267        if renames:
268            newname = renames[position]
269        else:
270            newname = objname
271
272        setattr(sys.modules[current], newname, newobj)
273
274TaskData = namedtuple("TaskData", [
275    "pn",
276    "taskname",
277    "fn",
278    "deps",
279    "provides",
280    "taskhash",
281    "unihash",
282    "hashfn",
283    "taskhash_deps",
284])
285