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