xref: /openbmc/openbmc/poky/bitbake/lib/bb/__init__.py (revision 92b42cb3)
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.0.1"
13
14import sys
15if sys.version_info < (3, 6, 0):
16    raise RuntimeError("Sorry, python 3.6.0 or later is required for this version of bitbake")
17
18
19class BBHandledException(Exception):
20    """
21    The big dilemma for generic bitbake code is what information to give the user
22    when an exception occurs. Any exception inheriting this base exception class
23    has already provided information to the user via some 'fired' message type such as
24    an explicitly fired event using bb.fire, or a bb.error message. If bitbake
25    encounters an exception derived from this class, no backtrace or other information
26    will be given to the user, its assumed the earlier event provided the relevant information.
27    """
28    pass
29
30import os
31import logging
32
33
34class NullHandler(logging.Handler):
35    def emit(self, record):
36        pass
37
38class BBLoggerMixin(object):
39    def __init__(self, *args, **kwargs):
40        # Does nothing to allow calling super() from derived classes
41        pass
42
43    def setup_bblogger(self, name):
44        if name.split(".")[0] == "BitBake":
45            self.debug = self._debug_helper
46
47    def _debug_helper(self, *args, **kwargs):
48        return self.bbdebug(1, *args, **kwargs)
49
50    def debug2(self, *args, **kwargs):
51        return self.bbdebug(2, *args, **kwargs)
52
53    def debug3(self, *args, **kwargs):
54        return self.bbdebug(3, *args, **kwargs)
55
56    def bbdebug(self, level, msg, *args, **kwargs):
57        loglevel = logging.DEBUG - level + 1
58        if not bb.event.worker_pid:
59            if self.name in bb.msg.loggerDefaultDomains and loglevel > (bb.msg.loggerDefaultDomains[self.name]):
60                return
61            if loglevel < bb.msg.loggerDefaultLogLevel:
62                return
63        return self.log(loglevel, msg, *args, **kwargs)
64
65    def plain(self, msg, *args, **kwargs):
66        return self.log(logging.INFO + 1, msg, *args, **kwargs)
67
68    def verbose(self, msg, *args, **kwargs):
69        return self.log(logging.INFO - 1, msg, *args, **kwargs)
70
71    def verbnote(self, msg, *args, **kwargs):
72        return self.log(logging.INFO + 2, msg, *args, **kwargs)
73
74    def warnonce(self, msg, *args, **kwargs):
75        return self.log(logging.WARNING - 1, msg, *args, **kwargs)
76
77    def erroronce(self, msg, *args, **kwargs):
78        return self.log(logging.ERROR - 1, msg, *args, **kwargs)
79
80
81Logger = logging.getLoggerClass()
82class BBLogger(Logger, BBLoggerMixin):
83    def __init__(self, name, *args, **kwargs):
84        self.setup_bblogger(name)
85        super().__init__(name, *args, **kwargs)
86
87logging.raiseExceptions = False
88logging.setLoggerClass(BBLogger)
89
90class BBLoggerAdapter(logging.LoggerAdapter, BBLoggerMixin):
91    def __init__(self, logger, *args, **kwargs):
92        self.setup_bblogger(logger.name)
93        super().__init__(logger, *args, **kwargs)
94
95    if sys.version_info < (3, 6):
96        # These properties were added in Python 3.6. Add them in older versions
97        # for compatibility
98        @property
99        def manager(self):
100            return self.logger.manager
101
102        @manager.setter
103        def manager(self, value):
104            self.logger.manager = value
105
106        @property
107        def name(self):
108            return self.logger.name
109
110        def __repr__(self):
111            logger = self.logger
112            level = logger.getLevelName(logger.getEffectiveLevel())
113            return '<%s %s (%s)>' % (self.__class__.__name__, logger.name, level)
114
115logging.LoggerAdapter = BBLoggerAdapter
116
117logger = logging.getLogger("BitBake")
118logger.addHandler(NullHandler())
119logger.setLevel(logging.DEBUG - 2)
120
121mainlogger = logging.getLogger("BitBake.Main")
122
123class PrefixLoggerAdapter(logging.LoggerAdapter):
124    def __init__(self, prefix, logger):
125        super().__init__(logger, {})
126        self.__msg_prefix = prefix
127
128    def process(self, msg, kwargs):
129        return "%s%s" %(self.__msg_prefix, msg), kwargs
130
131# This has to be imported after the setLoggerClass, as the import of bb.msg
132# can result in construction of the various loggers.
133import bb.msg
134
135from bb import fetch2 as fetch
136sys.modules['bb.fetch'] = sys.modules['bb.fetch2']
137
138# Messaging convenience functions
139def plain(*args):
140    mainlogger.plain(''.join(args))
141
142def debug(lvl, *args):
143    if isinstance(lvl, str):
144        mainlogger.warning("Passed invalid debug level '%s' to bb.debug", lvl)
145        args = (lvl,) + args
146        lvl = 1
147    mainlogger.bbdebug(lvl, ''.join(args))
148
149def note(*args):
150    mainlogger.info(''.join(args))
151
152#
153# A higher prioity note which will show on the console but isn't a warning
154#
155# Something is happening the user should be aware of but they probably did
156# something to make it happen
157#
158def verbnote(*args):
159    mainlogger.verbnote(''.join(args))
160
161#
162# Warnings - things the user likely needs to pay attention to and fix
163#
164def warn(*args):
165    mainlogger.warning(''.join(args))
166
167def warnonce(*args):
168    mainlogger.warnonce(''.join(args))
169
170def error(*args, **kwargs):
171    mainlogger.error(''.join(args), extra=kwargs)
172
173def erroronce(*args):
174    mainlogger.erroronce(''.join(args))
175
176def fatal(*args, **kwargs):
177    mainlogger.critical(''.join(args), extra=kwargs)
178    raise BBHandledException()
179
180def deprecated(func, name=None, advice=""):
181    """This is a decorator which can be used to mark functions
182    as deprecated. It will result in a warning being emitted
183    when the function is used."""
184    import warnings
185
186    if advice:
187        advice = ": %s" % advice
188    if name is None:
189        name = func.__name__
190
191    def newFunc(*args, **kwargs):
192        warnings.warn("Call to deprecated function %s%s." % (name,
193                                                             advice),
194                      category=DeprecationWarning,
195                      stacklevel=2)
196        return func(*args, **kwargs)
197    newFunc.__name__ = func.__name__
198    newFunc.__doc__ = func.__doc__
199    newFunc.__dict__.update(func.__dict__)
200    return newFunc
201
202# For compatibility
203def deprecate_import(current, modulename, fromlist, renames = None):
204    """Import objects from one module into another, wrapping them with a DeprecationWarning"""
205    import sys
206
207    module = __import__(modulename, fromlist = fromlist)
208    for position, objname in enumerate(fromlist):
209        obj = getattr(module, objname)
210        newobj = deprecated(obj, "{0}.{1}".format(current, objname),
211                            "Please use {0}.{1} instead".format(modulename, objname))
212        if renames:
213            newname = renames[position]
214        else:
215            newname = objname
216
217        setattr(sys.modules[current], newname, newobj)
218
219