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