xref: /openbmc/openbmc/poky/bitbake/lib/bb/__init__.py (revision 1e42709b)
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__ = "1.51.0"
13
14import sys
15if sys.version_info < (3, 5, 0):
16    raise RuntimeError("Sorry, python 3.5.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
74Logger = logging.getLoggerClass()
75class BBLogger(Logger, BBLoggerMixin):
76    def __init__(self, name, *args, **kwargs):
77        self.setup_bblogger(name)
78        super().__init__(name, *args, **kwargs)
79
80logging.raiseExceptions = False
81logging.setLoggerClass(BBLogger)
82
83class BBLoggerAdapter(logging.LoggerAdapter, BBLoggerMixin):
84    def __init__(self, logger, *args, **kwargs):
85        self.setup_bblogger(logger.name)
86        super().__init__(logger, *args, **kwargs)
87
88    if sys.version_info < (3, 6):
89        # These properties were added in Python 3.6. Add them in older versions
90        # for compatibility
91        @property
92        def manager(self):
93            return self.logger.manager
94
95        @manager.setter
96        def manager(self, value):
97            self.logger.manager = value
98
99        @property
100        def name(self):
101            return self.logger.name
102
103        def __repr__(self):
104            logger = self.logger
105            level = logger.getLevelName(logger.getEffectiveLevel())
106            return '<%s %s (%s)>' % (self.__class__.__name__, logger.name, level)
107
108logging.LoggerAdapter = BBLoggerAdapter
109
110logger = logging.getLogger("BitBake")
111logger.addHandler(NullHandler())
112logger.setLevel(logging.DEBUG - 2)
113
114mainlogger = logging.getLogger("BitBake.Main")
115
116class PrefixLoggerAdapter(logging.LoggerAdapter):
117    def __init__(self, prefix, logger):
118        super().__init__(logger, {})
119        self.__msg_prefix = prefix
120
121    def process(self, msg, kwargs):
122        return "%s%s" %(self.__msg_prefix, msg), kwargs
123
124# This has to be imported after the setLoggerClass, as the import of bb.msg
125# can result in construction of the various loggers.
126import bb.msg
127
128from bb import fetch2 as fetch
129sys.modules['bb.fetch'] = sys.modules['bb.fetch2']
130
131# Messaging convenience functions
132def plain(*args):
133    mainlogger.plain(''.join(args))
134
135def debug(lvl, *args):
136    if isinstance(lvl, str):
137        mainlogger.warning("Passed invalid debug level '%s' to bb.debug", lvl)
138        args = (lvl,) + args
139        lvl = 1
140    mainlogger.bbdebug(lvl, ''.join(args))
141
142def note(*args):
143    mainlogger.info(''.join(args))
144
145#
146# A higher prioity note which will show on the console but isn't a warning
147#
148# Something is happening the user should be aware of but they probably did
149# something to make it happen
150#
151def verbnote(*args):
152    mainlogger.verbnote(''.join(args))
153
154#
155# Warnings - things the user likely needs to pay attention to and fix
156#
157def warn(*args):
158    mainlogger.warning(''.join(args))
159
160def error(*args, **kwargs):
161    mainlogger.error(''.join(args), extra=kwargs)
162
163def fatal(*args, **kwargs):
164    mainlogger.critical(''.join(args), extra=kwargs)
165    raise BBHandledException()
166
167def deprecated(func, name=None, advice=""):
168    """This is a decorator which can be used to mark functions
169    as deprecated. It will result in a warning being emitted
170    when the function is used."""
171    import warnings
172
173    if advice:
174        advice = ": %s" % advice
175    if name is None:
176        name = func.__name__
177
178    def newFunc(*args, **kwargs):
179        warnings.warn("Call to deprecated function %s%s." % (name,
180                                                             advice),
181                      category=DeprecationWarning,
182                      stacklevel=2)
183        return func(*args, **kwargs)
184    newFunc.__name__ = func.__name__
185    newFunc.__doc__ = func.__doc__
186    newFunc.__dict__.update(func.__dict__)
187    return newFunc
188
189# For compatibility
190def deprecate_import(current, modulename, fromlist, renames = None):
191    """Import objects from one module into another, wrapping them with a DeprecationWarning"""
192    import sys
193
194    module = __import__(modulename, fromlist = fromlist)
195    for position, objname in enumerate(fromlist):
196        obj = getattr(module, objname)
197        newobj = deprecated(obj, "{0}.{1}".format(current, objname),
198                            "Please use {0}.{1} instead".format(modulename, objname))
199        if renames:
200            newname = renames[position]
201        else:
202            newname = objname
203
204        setattr(sys.modules[current], newname, newobj)
205
206