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