Ejemplo n.º 1
0
    def __init__(self, output=sys.stdout, tracking=False):
        # Needed to avoid deprecation warnings with python 2.6
        warnings.filterwarnings("ignore", category=DeprecationWarning)

        # Set up logging
        self.logger = logging.getLogger('BitBake')
        console = logging.StreamHandler(output)
        bb.msg.addDefaultlogFilter(console)
        format = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
        if output.isatty():
            format.enable_color()
        console.setFormatter(format)
        self.logger.addHandler(console)

        self.config = CookerConfiguration()
        configparams = TinfoilConfigParameters(parse_only=True)
        self.config.setConfigParameters(configparams)
        self.config.setServerRegIdleCallback(self.register_idle_function)
        features = []
        if tracking:
            features.append(CookerFeatures.BASEDATASTORE_TRACKING)
        self.cooker = BBCooker(self.config, features)
        self.config_data = self.cooker.data
        bb.providers.logger.setLevel(logging.ERROR)
        self.cooker_data = None
Ejemplo n.º 2
0
class Tinfoil:
    def __init__(self, output=sys.stdout, tracking=False):
        # Needed to avoid deprecation warnings with python 2.6
        warnings.filterwarnings("ignore", category=DeprecationWarning)

        # Set up logging
        self.logger = logging.getLogger('BitBake')
        self._log_hdlr = logging.StreamHandler(output)
        bb.msg.addDefaultlogFilter(self._log_hdlr)
        format = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
        if output.isatty():
            format.enable_color()
        self._log_hdlr.setFormatter(format)
        self.logger.addHandler(self._log_hdlr)

        self.config = CookerConfiguration()
        configparams = TinfoilConfigParameters(parse_only=True)
        self.config.setConfigParameters(configparams)
        self.config.setServerRegIdleCallback(self.register_idle_function)
        features = []
        if tracking:
            features.append(CookerFeatures.BASEDATASTORE_TRACKING)
        self.cooker = BBCooker(self.config, features)
        self.config_data = self.cooker.data
        bb.providers.logger.setLevel(logging.ERROR)
        self.cooker_data = None

    def register_idle_function(self, function, data):
        pass

    def parseRecipes(self):
        sys.stderr.write("Parsing recipes..")
        self.logger.setLevel(logging.WARNING)

        try:
            while self.cooker.state in (state.initial, state.parsing):
                self.cooker.updateCache()
        except KeyboardInterrupt:
            self.cooker.shutdown()
            self.cooker.updateCache()
            sys.exit(2)

        self.logger.setLevel(logging.INFO)
        sys.stderr.write("done.\n")

        self.cooker_data = self.cooker.recipecache

    def prepare(self, config_only=False):
        if not self.cooker_data:
            if config_only:
                self.cooker.parseConfiguration()
                self.cooker_data = self.cooker.recipecache
            else:
                self.parseRecipes()

    def shutdown(self):
        self.cooker.shutdown(force=True)
        self.cooker.post_serve()
        self.cooker.unlockBitbake()
        self.logger.removeHandler(self._log_hdlr)
Ejemplo n.º 3
0
class Tinfoil:
    def __init__(self, output=sys.stdout, tracking=False):
        # Needed to avoid deprecation warnings with python 2.6
        warnings.filterwarnings("ignore", category=DeprecationWarning)

        # Set up logging
        self.logger = logging.getLogger("BitBake")
        self._log_hdlr = logging.StreamHandler(output)
        bb.msg.addDefaultlogFilter(self._log_hdlr)
        format = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
        if output.isatty():
            format.enable_color()
        self._log_hdlr.setFormatter(format)
        self.logger.addHandler(self._log_hdlr)

        self.config = CookerConfiguration()
        configparams = TinfoilConfigParameters(parse_only=True)
        self.config.setConfigParameters(configparams)
        self.config.setServerRegIdleCallback(self.register_idle_function)
        features = []
        if tracking:
            features.append(CookerFeatures.BASEDATASTORE_TRACKING)
        self.cooker = BBCooker(self.config, features)
        self.config_data = self.cooker.data
        bb.providers.logger.setLevel(logging.ERROR)
        self.cooker_data = None

    def register_idle_function(self, function, data):
        pass

    def parseRecipes(self):
        sys.stderr.write("Parsing recipes..")
        self.logger.setLevel(logging.WARNING)

        try:
            while self.cooker.state in (state.initial, state.parsing):
                self.cooker.updateCache()
        except KeyboardInterrupt:
            self.cooker.shutdown()
            self.cooker.updateCache()
            sys.exit(2)

        self.logger.setLevel(logging.INFO)
        sys.stderr.write("done.\n")

        self.cooker_data = self.cooker.recipecaches[""]

    def prepare(self, config_only=False):
        if not self.cooker_data:
            if config_only:
                self.cooker.parseConfiguration()
                self.cooker_data = self.cooker.recipecaches[""]
            else:
                self.parseRecipes()

    def shutdown(self):
        self.cooker.shutdown(force=True)
        self.cooker.post_serve()
        self.cooker.unlockBitbake()
        self.logger.removeHandler(self._log_hdlr)
Ejemplo n.º 4
0
Archivo: tinfoil.py Proyecto: kacf/poky
    def prepare(self, config_only=False, config_params=None, quiet=0):
        if self.tracking:
            extrafeatures = [bb.cooker.CookerFeatures.BASEDATASTORE_TRACKING]
        else:
            extrafeatures = []

        if not config_params:
            config_params = TinfoilConfigParameters(config_only=config_only, quiet=quiet)

        cookerconfig = CookerConfiguration()
        cookerconfig.setConfigParameters(config_params)

        server, self.server_connection, ui_module = setup_bitbake(config_params,
                            cookerconfig,
                            extrafeatures)

        self.ui_module = ui_module

        if self.server_connection:
            _server_connections.append(self.server_connection)
            if config_only:
                config_params.updateToServer(self.server_connection.connection, os.environ.copy())
                self.run_command('parseConfiguration')
            else:
                self.run_actions(config_params)

            self.config_data = bb.data.init()
            connector = TinfoilDataStoreConnector(self, None)
            self.config_data.setVar('_remote_data', connector)
            self.cooker = TinfoilCookerAdapter(self)
            self.cooker_data = self.cooker.recipecaches['']
        else:
            raise Exception('Failed to start bitbake server')
Ejemplo n.º 5
0
    def prepare(self,
                config_only=False,
                config_params=None,
                quiet=0,
                extra_features=None):
        if self.tracking:
            extrafeatures = [bb.cooker.CookerFeatures.BASEDATASTORE_TRACKING]
        else:
            extrafeatures = []

        if extra_features:
            extrafeatures += extra_features

        if not config_params:
            config_params = TinfoilConfigParameters(config_only=config_only,
                                                    quiet=quiet)

        cookerconfig = CookerConfiguration()
        cookerconfig.setConfigParameters(config_params)

        server, self.server_connection, ui_module = setup_bitbake(
            config_params, cookerconfig, extrafeatures, setup_logging=False)

        self.ui_module = ui_module

        # Ensure the path to bitbake's bin directory is in PATH so that things like
        # bitbake-worker can be run (usually this is the case, but it doesn't have to be)
        path = os.getenv('PATH').split(':')
        bitbakebinpath = os.path.abspath(
            os.path.join(os.path.dirname(os.path.abspath(__file__)), '..',
                         '..', 'bin'))
        for entry in path:
            if entry.endswith(os.sep):
                entry = entry[:-1]
            if os.path.abspath(entry) == bitbakebinpath:
                break
        else:
            path.insert(0, bitbakebinpath)
            os.environ['PATH'] = ':'.join(path)

        if self.server_connection:
            _server_connections.append(self.server_connection)
            if config_only:
                config_params.updateToServer(self.server_connection.connection,
                                             os.environ.copy())
                self.run_command('parseConfiguration')
            else:
                self.run_actions(config_params)

            self.config_data = bb.data.init()
            connector = TinfoilDataStoreConnector(self, None)
            self.config_data.setVar('_remote_data', connector)
            self.cooker = TinfoilCookerAdapter(self)
            self.cooker_data = self.cooker.recipecaches['']
        else:
            raise Exception('Failed to start bitbake server')
Ejemplo n.º 6
0
    def __init__(self, output=sys.stdout, tracking=False):
        # Needed to avoid deprecation warnings with python 2.6
        warnings.filterwarnings("ignore", category=DeprecationWarning)

        # Set up logging
        self.logger = logging.getLogger('BitBake')
        console = logging.StreamHandler(output)
        bb.msg.addDefaultlogFilter(console)
        format = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
        if output.isatty():
            format.enable_color()
        console.setFormatter(format)
        self.logger.addHandler(console)

        self.config = CookerConfiguration()
        configparams = TinfoilConfigParameters(parse_only=True)
        self.config.setConfigParameters(configparams)
        self.config.setServerRegIdleCallback(self.register_idle_function)
        features = []
        if tracking:
            features.append(CookerFeatures.BASEDATASTORE_TRACKING)
        self.cooker = BBCooker(self.config, features)
        self.config_data = self.cooker.data
        bb.providers.logger.setLevel(logging.ERROR)
        self.cooker_data = None
Ejemplo n.º 7
0
    def prepare(self, config_only=False, config_params=None, quiet=0):
        if self.tracking:
            extrafeatures = [bb.cooker.CookerFeatures.BASEDATASTORE_TRACKING]
        else:
            extrafeatures = []

        if not config_params:
            config_params = TinfoilConfigParameters(config_only=config_only, quiet=quiet)

        cookerconfig = CookerConfiguration()
        cookerconfig.setConfigParameters(config_params)

        server, self.server_connection, ui_module = setup_bitbake(config_params,
                            cookerconfig,
                            extrafeatures)

        self.ui_module = ui_module

        # Ensure the path to bitbake's bin directory is in PATH so that things like
        # bitbake-worker can be run (usually this is the case, but it doesn't have to be)
        path = os.getenv('PATH').split(':')
        bitbakebinpath = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..', 'bin'))
        for entry in path:
            if entry.endswith(os.sep):
                entry = entry[:-1]
            if os.path.abspath(entry) == bitbakebinpath:
                break
        else:
            path.insert(0, bitbakebinpath)
            os.environ['PATH'] = ':'.join(path)

        if self.server_connection:
            _server_connections.append(self.server_connection)
            if config_only:
                config_params.updateToServer(self.server_connection.connection, os.environ.copy())
                self.run_command('parseConfiguration')
            else:
                self.run_actions(config_params)

            self.config_data = bb.data.init()
            connector = TinfoilDataStoreConnector(self, None)
            self.config_data.setVar('_remote_data', connector)
            self.cooker = TinfoilCookerAdapter(self)
            self.cooker_data = self.cooker.recipecaches['']
        else:
            raise Exception('Failed to start bitbake server')
Ejemplo n.º 8
0
    def __init__(self, output=sys.stdout):
        # Needed to avoid deprecation warnings with python 2.6
        warnings.filterwarnings("ignore", category=DeprecationWarning)

        # Set up logging
        self.logger = logging.getLogger('BitBake')
        if output is not None:
            setup_log_handler(self.logger, output)

        self.config = self.config = CookerConfiguration()
        configparams = bb.tinfoil.TinfoilConfigParameters(parse_only=True)
        self.config.setConfigParameters(configparams)
        self.config.setServerRegIdleCallback(self.register_idle_function)
        self.cooker = bb.cooker.BBCooker(self.config)
        self.config_data = self.cooker.data
        bb.providers.logger.setLevel(logging.ERROR)
        bb.taskdata.logger.setLevel(logging.CRITICAL)
        self.cooker_data = None
        self.taskdata = None

        self.localdata = bb.data.createCopy(self.config_data)
        self.localdata.finalize()
        # TODO: why isn't expandKeys a method of DataSmart?
        bb.data.expandKeys(self.localdata)
Ejemplo n.º 9
0
    def prepare(self,
                config_only=False,
                config_params=None,
                quiet=0,
                extra_features=None):
        """
        Prepares the underlying BitBake system to be used via tinfoil.
        This function must be called prior to calling any of the other
        functions in the API.
        NOTE: if you call prepare() you must absolutely call shutdown()
        before your code terminates. You can use a "with" block to ensure
        this happens e.g.

            with bb.tinfoil.Tinfoil() as tinfoil:
                tinfoil.prepare()
                ...

        Parameters:
            config_only: True to read only the configuration and not load
                        the cache / parse recipes. This is useful if you just
                        want to query the value of a variable at the global
                        level or you want to do anything else that doesn't
                        involve knowing anything about the recipes in the
                        current configuration. False loads the cache / parses
                        recipes.
            config_params: optionally specify your own configuration
                        parameters. If not specified an instance of
                        TinfoilConfigParameters will be created internally.
            quiet:      quiet level controlling console output - equivalent
                        to bitbake's -q/--quiet option. Default of 0 gives
                        the same output level as normal bitbake execution.
            extra_features: extra features to be added to the feature
                        set requested from the server. See
                        CookerFeatures._feature_list for possible
                        features.
        """
        self.quiet = quiet

        if self.tracking:
            extrafeatures = [bb.cooker.CookerFeatures.BASEDATASTORE_TRACKING]
        else:
            extrafeatures = []

        if extra_features:
            extrafeatures += extra_features

        if not config_params:
            config_params = TinfoilConfigParameters(config_only=config_only,
                                                    quiet=quiet)

        cookerconfig = CookerConfiguration()
        cookerconfig.setConfigParameters(config_params)

        if not config_only:
            # Disable local loggers because the UI module is going to set up its own
            for handler in self.localhandlers:
                self.logger.handlers.remove(handler)
            self.localhandlers = []

        self.server_connection, ui_module = setup_bitbake(
            config_params, cookerconfig, extrafeatures)

        self.ui_module = ui_module

        # Ensure the path to bitbake's bin directory is in PATH so that things like
        # bitbake-worker can be run (usually this is the case, but it doesn't have to be)
        path = os.getenv('PATH').split(':')
        bitbakebinpath = os.path.abspath(
            os.path.join(os.path.dirname(os.path.abspath(__file__)), '..',
                         '..', 'bin'))
        for entry in path:
            if entry.endswith(os.sep):
                entry = entry[:-1]
            if os.path.abspath(entry) == bitbakebinpath:
                break
        else:
            path.insert(0, bitbakebinpath)
            os.environ['PATH'] = ':'.join(path)

        if self.server_connection:
            _server_connections.append(self.server_connection)
            if config_only:
                config_params.updateToServer(self.server_connection.connection,
                                             os.environ.copy())
                self.run_command('parseConfiguration')
            else:
                self.run_actions(config_params)
                self.recipes_parsed = True

            self.config_data = TinfoilDataStoreConnector(self, 0)
            self.cooker = TinfoilCookerAdapter(self)
            self.cooker_data = self.cooker.recipecaches['']
        else:
            raise Exception('Failed to start bitbake server')
Ejemplo n.º 10
0
    def prepare(self, config_only=False, config_params=None, quiet=0, extra_features=None):
        """
        Prepares the underlying BitBake system to be used via tinfoil.
        This function must be called prior to calling any of the other
        functions in the API.
        NOTE: if you call prepare() you must absolutely call shutdown()
        before your code terminates. You can use a "with" block to ensure
        this happens e.g.

            with bb.tinfoil.Tinfoil() as tinfoil:
                tinfoil.prepare()
                ...

        Parameters:
            config_only: True to read only the configuration and not load
                        the cache / parse recipes. This is useful if you just
                        want to query the value of a variable at the global
                        level or you want to do anything else that doesn't
                        involve knowing anything about the recipes in the
                        current configuration. False loads the cache / parses
                        recipes.
            config_params: optionally specify your own configuration
                        parameters. If not specified an instance of
                        TinfoilConfigParameters will be created internally.
            quiet:      quiet level controlling console output - equivalent
                        to bitbake's -q/--quiet option. Default of 0 gives
                        the same output level as normal bitbake execution.
            extra_features: extra features to be added to the feature
                        set requested from the server. See
                        CookerFeatures._feature_list for possible
                        features.
        """
        self.quiet = quiet

        if self.tracking:
            extrafeatures = [bb.cooker.CookerFeatures.BASEDATASTORE_TRACKING]
        else:
            extrafeatures = []

        if extra_features:
            extrafeatures += extra_features

        if not config_params:
            config_params = TinfoilConfigParameters(config_only=config_only, quiet=quiet)

        cookerconfig = CookerConfiguration()
        cookerconfig.setConfigParameters(config_params)

        if not config_only:
            # Disable local loggers because the UI module is going to set up its own
            for handler in self.localhandlers:
                self.logger.handlers.remove(handler)
            self.localhandlers = []

        self.server_connection, ui_module = setup_bitbake(config_params,
                            cookerconfig,
                            extrafeatures)

        self.ui_module = ui_module

        # Ensure the path to bitbake's bin directory is in PATH so that things like
        # bitbake-worker can be run (usually this is the case, but it doesn't have to be)
        path = os.getenv('PATH').split(':')
        bitbakebinpath = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..', 'bin'))
        for entry in path:
            if entry.endswith(os.sep):
                entry = entry[:-1]
            if os.path.abspath(entry) == bitbakebinpath:
                break
        else:
            path.insert(0, bitbakebinpath)
            os.environ['PATH'] = ':'.join(path)

        if self.server_connection:
            _server_connections.append(self.server_connection)
            if config_only:
                config_params.updateToServer(self.server_connection.connection, os.environ.copy())
                self.run_command('parseConfiguration')
            else:
                self.run_actions(config_params)
                self.recipes_parsed = True

            self.config_data = bb.data.init()
            connector = TinfoilDataStoreConnector(self, None)
            self.config_data.setVar('_remote_data', connector)
            self.cooker = TinfoilCookerAdapter(self)
            self.cooker_data = self.cooker.recipecaches['']
        else:
            raise Exception('Failed to start bitbake server')
Ejemplo n.º 11
0
class Tinfoil:
    def __init__(self, output=sys.stdout, tracking=False):
        # Needed to avoid deprecation warnings with python 2.6
        warnings.filterwarnings("ignore", category=DeprecationWarning)

        # Set up logging
        self.logger = logging.getLogger('BitBake')
        self._log_hdlr = logging.StreamHandler(output)
        bb.msg.addDefaultlogFilter(self._log_hdlr)
        format = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
        if output.isatty():
            format.enable_color()
        self._log_hdlr.setFormatter(format)
        self.logger.addHandler(self._log_hdlr)

        self.config = CookerConfiguration()
        configparams = TinfoilConfigParameters(parse_only=True)
        self.config.setConfigParameters(configparams)
        self.config.setServerRegIdleCallback(self.register_idle_function)
        features = []
        if tracking:
            features.append(CookerFeatures.BASEDATASTORE_TRACKING)
        self.cooker = BBCooker(self.config, features)
        self.config_data = self.cooker.data
        bb.providers.logger.setLevel(logging.ERROR)
        self.cooker_data = None

    def register_idle_function(self, function, data):
        pass

    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        self.shutdown()

    def parseRecipes(self):
        sys.stderr.write("Parsing recipes..")
        self.logger.setLevel(logging.WARNING)

        try:
            while self.cooker.state in (state.initial, state.parsing):
                self.cooker.updateCache()
        except KeyboardInterrupt:
            self.cooker.shutdown()
            self.cooker.updateCache()
            sys.exit(2)

        self.logger.setLevel(logging.INFO)
        sys.stderr.write("done.\n")

        self.cooker_data = self.cooker.recipecaches['']

    def prepare(self, config_only=False):
        if not self.cooker_data:
            if config_only:
                self.cooker.parseConfiguration()
                self.cooker_data = self.cooker.recipecaches['']
            else:
                self.parseRecipes()

    def parse_recipe_file(self,
                          fn,
                          appends=True,
                          appendlist=None,
                          config_data=None):
        """
        Parse the specified recipe file (with or without bbappends)
        and return a datastore object representing the environment
        for the recipe.
        Parameters:
            fn: recipe file to parse - can be a file path or virtual
                specification
            appends: True to apply bbappends, False otherwise
            appendlist: optional list of bbappend files to apply, if you
                        want to filter them
            config_data: custom config datastore to use. NOTE: if you
                         specify config_data then you cannot use a virtual
                         specification for fn.
        """
        if appends and appendlist == []:
            appends = False
        if appends:
            if appendlist:
                appendfiles = appendlist
            else:
                if not hasattr(self.cooker, 'collection'):
                    raise Exception(
                        'You must call tinfoil.prepare() with config_only=False in order to get bbappends'
                    )
                appendfiles = self.cooker.collection.get_file_appends(fn)
        else:
            appendfiles = None
        if config_data:
            # We have to use a different function here if we're passing in a datastore
            localdata = bb.data.createCopy(config_data)
            envdata = bb.cache.parse_recipe(localdata, fn, appendfiles)['']
        else:
            # Use the standard path
            parser = bb.cache.NoCache(self.cooker.databuilder)
            envdata = parser.loadDataFull(fn, appendfiles)
        return envdata

    def shutdown(self):
        self.cooker.shutdown(force=True)
        self.cooker.post_serve()
        self.cooker.unlockBitbake()
        self.logger.removeHandler(self._log_hdlr)
Ejemplo n.º 12
0
class Tinfoil:
    def __init__(self, output=sys.stdout, tracking=False):
        # Needed to avoid deprecation warnings with python 2.6
        warnings.filterwarnings("ignore", category=DeprecationWarning)

        # Set up logging
        self.logger = logging.getLogger('BitBake')
        self._log_hdlr = logging.StreamHandler(output)
        bb.msg.addDefaultlogFilter(self._log_hdlr)
        format = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
        if output.isatty():
            format.enable_color()
        self._log_hdlr.setFormatter(format)
        self.logger.addHandler(self._log_hdlr)

        self.config = CookerConfiguration()
        configparams = TinfoilConfigParameters(parse_only=True)
        self.config.setConfigParameters(configparams)
        self.config.setServerRegIdleCallback(self.register_idle_function)
        features = []
        if tracking:
            features.append(CookerFeatures.BASEDATASTORE_TRACKING)
        self.cooker = BBCooker(self.config, features)
        self.config_data = self.cooker.data
        bb.providers.logger.setLevel(logging.ERROR)
        self.cooker_data = None

    def register_idle_function(self, function, data):
        pass

    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        self.shutdown()

    def parseRecipes(self):
        sys.stderr.write("Parsing recipes..")
        self.logger.setLevel(logging.WARNING)

        try:
            while self.cooker.state in (state.initial, state.parsing):
                self.cooker.updateCache()
        except KeyboardInterrupt:
            self.cooker.shutdown()
            self.cooker.updateCache()
            sys.exit(2)

        self.logger.setLevel(logging.INFO)
        sys.stderr.write("done.\n")

        self.cooker_data = self.cooker.recipecaches['']

    def prepare(self, config_only = False):
        if not self.cooker_data:
            if config_only:
                self.cooker.parseConfiguration()
                self.cooker_data = self.cooker.recipecaches['']
            else:
                self.parseRecipes()

    def parse_recipe_file(self, fn, appends=True, appendlist=None, config_data=None):
        """
        Parse the specified recipe file (with or without bbappends)
        and return a datastore object representing the environment
        for the recipe.
        Parameters:
            fn: recipe file to parse - can be a file path or virtual
                specification
            appends: True to apply bbappends, False otherwise
            appendlist: optional list of bbappend files to apply, if you
                        want to filter them
            config_data: custom config datastore to use. NOTE: if you
                         specify config_data then you cannot use a virtual
                         specification for fn.
        """
        if appends and appendlist == []:
            appends = False
        if appends:
            if appendlist:
                appendfiles = appendlist
            else:
                if not hasattr(self.cooker, 'collection'):
                    raise Exception('You must call tinfoil.prepare() with config_only=False in order to get bbappends')
                appendfiles = self.cooker.collection.get_file_appends(fn)
        else:
            appendfiles = None
        if config_data:
            # We have to use a different function here if we're passing in a datastore
            localdata = bb.data.createCopy(config_data)
            envdata = bb.cache.parse_recipe(localdata, fn, appendfiles)['']
        else:
            # Use the standard path
            parser = bb.cache.NoCache(self.cooker.databuilder)
            envdata = parser.loadDataFull(fn, appendfiles)
        return envdata

    def shutdown(self):
        self.cooker.shutdown(force=True)
        self.cooker.post_serve()
        self.cooker.unlockBitbake()
        self.logger.removeHandler(self._log_hdlr)