diff options
Diffstat (limited to 'import-layers/yocto-poky/bitbake/lib/bb/tinfoil.py')
-rw-r--r-- | import-layers/yocto-poky/bitbake/lib/bb/tinfoil.py | 900 |
1 files changed, 0 insertions, 900 deletions
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/tinfoil.py b/import-layers/yocto-poky/bitbake/lib/bb/tinfoil.py deleted file mode 100644 index 368264f39..000000000 --- a/import-layers/yocto-poky/bitbake/lib/bb/tinfoil.py +++ /dev/null @@ -1,900 +0,0 @@ -# tinfoil: a simple wrapper around cooker for bitbake-based command-line utilities -# -# Copyright (C) 2012-2017 Intel Corporation -# Copyright (C) 2011 Mentor Graphics Corporation -# Copyright (C) 2006-2012 Richard Purdie -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -import logging -import os -import sys -import atexit -import re -from collections import OrderedDict, defaultdict - -import bb.cache -import bb.cooker -import bb.providers -import bb.taskdata -import bb.utils -import bb.command -import bb.remotedata -from bb.cookerdata import CookerConfiguration, ConfigParameters -from bb.main import setup_bitbake, BitBakeConfigParameters, BBMainException -import bb.fetch2 - - -# We need this in order to shut down the connection to the bitbake server, -# otherwise the process will never properly exit -_server_connections = [] -def _terminate_connections(): - for connection in _server_connections: - connection.terminate() -atexit.register(_terminate_connections) - -class TinfoilUIException(Exception): - """Exception raised when the UI returns non-zero from its main function""" - def __init__(self, returncode): - self.returncode = returncode - def __repr__(self): - return 'UI module main returned %d' % self.returncode - -class TinfoilCommandFailed(Exception): - """Exception raised when run_command fails""" - -class TinfoilDataStoreConnector: - """Connector object used to enable access to datastore objects via tinfoil""" - - def __init__(self, tinfoil, dsindex): - self.tinfoil = tinfoil - self.dsindex = dsindex - def getVar(self, name): - value = self.tinfoil.run_command('dataStoreConnectorFindVar', self.dsindex, name) - overrides = None - if isinstance(value, dict): - if '_connector_origtype' in value: - value['_content'] = self.tinfoil._reconvert_type(value['_content'], value['_connector_origtype']) - del value['_connector_origtype'] - if '_connector_overrides' in value: - overrides = value['_connector_overrides'] - del value['_connector_overrides'] - return value, overrides - def getKeys(self): - return set(self.tinfoil.run_command('dataStoreConnectorGetKeys', self.dsindex)) - def getVarHistory(self, name): - return self.tinfoil.run_command('dataStoreConnectorGetVarHistory', self.dsindex, name) - def expandPythonRef(self, varname, expr, d): - ds = bb.remotedata.RemoteDatastores.transmit_datastore(d) - ret = self.tinfoil.run_command('dataStoreConnectorExpandPythonRef', ds, varname, expr) - return ret - def setVar(self, varname, value): - if self.dsindex is None: - self.tinfoil.run_command('setVariable', varname, value) - else: - # Not currently implemented - indicate that setting should - # be redirected to local side - return True - def setVarFlag(self, varname, flagname, value): - if self.dsindex is None: - self.tinfoil.run_command('dataStoreConnectorSetVarFlag', self.dsindex, varname, flagname, value) - else: - # Not currently implemented - indicate that setting should - # be redirected to local side - return True - def delVar(self, varname): - if self.dsindex is None: - self.tinfoil.run_command('dataStoreConnectorDelVar', self.dsindex, varname) - else: - # Not currently implemented - indicate that setting should - # be redirected to local side - return True - def delVarFlag(self, varname, flagname): - if self.dsindex is None: - self.tinfoil.run_command('dataStoreConnectorDelVar', self.dsindex, varname, flagname) - else: - # Not currently implemented - indicate that setting should - # be redirected to local side - return True - def renameVar(self, name, newname): - if self.dsindex is None: - self.tinfoil.run_command('dataStoreConnectorRenameVar', self.dsindex, name, newname) - else: - # Not currently implemented - indicate that setting should - # be redirected to local side - return True - -class TinfoilCookerAdapter: - """ - Provide an adapter for existing code that expects to access a cooker object via Tinfoil, - since now Tinfoil is on the client side it no longer has direct access. - """ - - class TinfoilCookerCollectionAdapter: - """ cooker.collection adapter """ - def __init__(self, tinfoil): - self.tinfoil = tinfoil - def get_file_appends(self, fn): - return self.tinfoil.get_file_appends(fn) - def __getattr__(self, name): - if name == 'overlayed': - return self.tinfoil.get_overlayed_recipes() - elif name == 'bbappends': - return self.tinfoil.run_command('getAllAppends') - else: - raise AttributeError("%s instance has no attribute '%s'" % (self.__class__.__name__, name)) - - class TinfoilRecipeCacheAdapter: - """ cooker.recipecache adapter """ - def __init__(self, tinfoil): - self.tinfoil = tinfoil - self._cache = {} - - def get_pkg_pn_fn(self): - pkg_pn = defaultdict(list, self.tinfoil.run_command('getRecipes') or []) - pkg_fn = {} - for pn, fnlist in pkg_pn.items(): - for fn in fnlist: - pkg_fn[fn] = pn - self._cache['pkg_pn'] = pkg_pn - self._cache['pkg_fn'] = pkg_fn - - def __getattr__(self, name): - # Grab these only when they are requested since they aren't always used - if name in self._cache: - return self._cache[name] - elif name == 'pkg_pn': - self.get_pkg_pn_fn() - return self._cache[name] - elif name == 'pkg_fn': - self.get_pkg_pn_fn() - return self._cache[name] - elif name == 'deps': - attrvalue = defaultdict(list, self.tinfoil.run_command('getRecipeDepends') or []) - elif name == 'rundeps': - attrvalue = defaultdict(lambda: defaultdict(list), self.tinfoil.run_command('getRuntimeDepends') or []) - elif name == 'runrecs': - attrvalue = defaultdict(lambda: defaultdict(list), self.tinfoil.run_command('getRuntimeRecommends') or []) - elif name == 'pkg_pepvpr': - attrvalue = self.tinfoil.run_command('getRecipeVersions') or {} - elif name == 'inherits': - attrvalue = self.tinfoil.run_command('getRecipeInherits') or {} - elif name == 'bbfile_priority': - attrvalue = self.tinfoil.run_command('getBbFilePriority') or {} - elif name == 'pkg_dp': - attrvalue = self.tinfoil.run_command('getDefaultPreference') or {} - elif name == 'fn_provides': - attrvalue = self.tinfoil.run_command('getRecipeProvides') or {} - elif name == 'packages': - attrvalue = self.tinfoil.run_command('getRecipePackages') or {} - elif name == 'packages_dynamic': - attrvalue = self.tinfoil.run_command('getRecipePackagesDynamic') or {} - elif name == 'rproviders': - attrvalue = self.tinfoil.run_command('getRProviders') or {} - else: - raise AttributeError("%s instance has no attribute '%s'" % (self.__class__.__name__, name)) - - self._cache[name] = attrvalue - return attrvalue - - def __init__(self, tinfoil): - self.tinfoil = tinfoil - self.collection = self.TinfoilCookerCollectionAdapter(tinfoil) - self.recipecaches = {} - # FIXME all machines - self.recipecaches[''] = self.TinfoilRecipeCacheAdapter(tinfoil) - self._cache = {} - def __getattr__(self, name): - # Grab these only when they are requested since they aren't always used - if name in self._cache: - return self._cache[name] - elif name == 'skiplist': - attrvalue = self.tinfoil.get_skipped_recipes() - elif name == 'bbfile_config_priorities': - ret = self.tinfoil.run_command('getLayerPriorities') - bbfile_config_priorities = [] - for collection, pattern, regex, pri in ret: - bbfile_config_priorities.append((collection, pattern, re.compile(regex), pri)) - - attrvalue = bbfile_config_priorities - else: - raise AttributeError("%s instance has no attribute '%s'" % (self.__class__.__name__, name)) - - self._cache[name] = attrvalue - return attrvalue - - def findBestProvider(self, pn): - return self.tinfoil.find_best_provider(pn) - - -class TinfoilRecipeInfo: - """ - Provides a convenient representation of the cached information for a single recipe. - Some attributes are set on construction, others are read on-demand (which internally - may result in a remote procedure call to the bitbake server the first time). - Note that only information which is cached is available through this object - if - you need other variable values you will need to parse the recipe using - Tinfoil.parse_recipe(). - """ - def __init__(self, recipecache, d, pn, fn, fns): - self._recipecache = recipecache - self._d = d - self.pn = pn - self.fn = fn - self.fns = fns - self.inherit_files = recipecache.inherits[fn] - self.depends = recipecache.deps[fn] - (self.pe, self.pv, self.pr) = recipecache.pkg_pepvpr[fn] - self._cached_packages = None - self._cached_rprovides = None - self._cached_packages_dynamic = None - - def __getattr__(self, name): - if name == 'alternates': - return [x for x in self.fns if x != self.fn] - elif name == 'rdepends': - return self._recipecache.rundeps[self.fn] - elif name == 'rrecommends': - return self._recipecache.runrecs[self.fn] - elif name == 'provides': - return self._recipecache.fn_provides[self.fn] - elif name == 'packages': - if self._cached_packages is None: - self._cached_packages = [] - for pkg, fns in self._recipecache.packages.items(): - if self.fn in fns: - self._cached_packages.append(pkg) - return self._cached_packages - elif name == 'packages_dynamic': - if self._cached_packages_dynamic is None: - self._cached_packages_dynamic = [] - for pkg, fns in self._recipecache.packages_dynamic.items(): - if self.fn in fns: - self._cached_packages_dynamic.append(pkg) - return self._cached_packages_dynamic - elif name == 'rprovides': - if self._cached_rprovides is None: - self._cached_rprovides = [] - for pkg, fns in self._recipecache.rproviders.items(): - if self.fn in fns: - self._cached_rprovides.append(pkg) - return self._cached_rprovides - else: - raise AttributeError("%s instance has no attribute '%s'" % (self.__class__.__name__, name)) - def inherits(self, only_recipe=False): - """ - Get the inherited classes for a recipe. Returns the class names only. - Parameters: - only_recipe: True to return only the classes inherited by the recipe - itself, False to return all classes inherited within - the context for the recipe (which includes globally - inherited classes). - """ - if only_recipe: - global_inherit = [x for x in (self._d.getVar('BBINCLUDED') or '').split() if x.endswith('.bbclass')] - else: - global_inherit = [] - for clsfile in self.inherit_files: - if only_recipe and clsfile in global_inherit: - continue - clsname = os.path.splitext(os.path.basename(clsfile))[0] - yield clsname - def __str__(self): - return '%s' % self.pn - - -class Tinfoil: - """ - Tinfoil - an API for scripts and utilities to query - BitBake internals and perform build operations. - """ - - def __init__(self, output=sys.stdout, tracking=False, setup_logging=True): - """ - Create a new tinfoil object. - Parameters: - output: specifies where console output should be sent. Defaults - to sys.stdout. - tracking: True to enable variable history tracking, False to - disable it (default). Enabling this has a minor - performance impact so typically it isn't enabled - unless you need to query variable history. - setup_logging: True to setup a logger so that things like - bb.warn() will work immediately and timeout warnings - are visible; False to let BitBake do this itself. - """ - self.logger = logging.getLogger('BitBake') - self.config_data = None - self.cooker = None - self.tracking = tracking - self.ui_module = None - self.server_connection = None - self.recipes_parsed = False - self.quiet = 0 - self.oldhandlers = self.logger.handlers[:] - if setup_logging: - # This is the *client-side* logger, nothing to do with - # logging messages from the server - bb.msg.logger_create('BitBake', output) - self.localhandlers = [] - for handler in self.logger.handlers: - if handler not in self.oldhandlers: - self.localhandlers.append(handler) - - def __enter__(self): - return self - - def __exit__(self, type, value, traceback): - self.shutdown() - - 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') - - def run_actions(self, config_params): - """ - Run the actions specified in config_params through the UI. - """ - ret = self.ui_module.main(self.server_connection.connection, self.server_connection.events, config_params) - if ret: - raise TinfoilUIException(ret) - - def parseRecipes(self): - """ - Legacy function - use parse_recipes() instead. - """ - self.parse_recipes() - - def parse_recipes(self): - """ - Load information on all recipes. Normally you should specify - config_only=False when calling prepare() instead of using this - function; this function is designed for situations where you need - to initialise Tinfoil and use it with config_only=True first and - then conditionally call this function to parse recipes later. - """ - config_params = TinfoilConfigParameters(config_only=False) - self.run_actions(config_params) - self.recipes_parsed = True - - def run_command(self, command, *params): - """ - Run a command on the server (as implemented in bb.command). - Note that there are two types of command - synchronous and - asynchronous; in order to receive the results of asynchronous - commands you will need to set an appropriate event mask - using set_event_mask() and listen for the result using - wait_event() - with the correct event mask you'll at least get - bb.command.CommandCompleted and possibly other events before - that depending on the command. - """ - if not self.server_connection: - raise Exception('Not connected to server (did you call .prepare()?)') - - commandline = [command] - if params: - commandline.extend(params) - result = self.server_connection.connection.runCommand(commandline) - if result[1]: - raise TinfoilCommandFailed(result[1]) - return result[0] - - def set_event_mask(self, eventlist): - """Set the event mask which will be applied within wait_event()""" - if not self.server_connection: - raise Exception('Not connected to server (did you call .prepare()?)') - llevel, debug_domains = bb.msg.constructLogOptions() - ret = self.run_command('setEventMask', self.server_connection.connection.getEventHandle(), llevel, debug_domains, eventlist) - if not ret: - raise Exception('setEventMask failed') - - def wait_event(self, timeout=0): - """ - Wait for an event from the server for the specified time. - A timeout of 0 means don't wait if there are no events in the queue. - Returns the next event in the queue or None if the timeout was - reached. Note that in order to recieve any events you will - first need to set the internal event mask using set_event_mask() - (otherwise whatever event mask the UI set up will be in effect). - """ - if not self.server_connection: - raise Exception('Not connected to server (did you call .prepare()?)') - return self.server_connection.events.waitEvent(timeout) - - def get_overlayed_recipes(self): - """ - Find recipes which are overlayed (i.e. where recipes exist in multiple layers) - """ - return defaultdict(list, self.run_command('getOverlayedRecipes')) - - def get_skipped_recipes(self): - """ - Find recipes which were skipped (i.e. SkipRecipe was raised - during parsing). - """ - return OrderedDict(self.run_command('getSkippedRecipes')) - - def get_all_providers(self): - return defaultdict(list, self.run_command('allProviders')) - - def find_providers(self): - return self.run_command('findProviders') - - def find_best_provider(self, pn): - return self.run_command('findBestProvider', pn) - - def get_runtime_providers(self, rdep): - return self.run_command('getRuntimeProviders', rdep) - - def get_recipe_file(self, pn): - """ - Get the file name for the specified recipe/target. Raises - bb.providers.NoProvider if there is no match or the recipe was - skipped. - """ - best = self.find_best_provider(pn) - if not best or (len(best) > 3 and not best[3]): - skiplist = self.get_skipped_recipes() - taskdata = bb.taskdata.TaskData(None, skiplist=skiplist) - skipreasons = taskdata.get_reasons(pn) - if skipreasons: - raise bb.providers.NoProvider('%s is unavailable:\n %s' % (pn, ' \n'.join(skipreasons))) - else: - raise bb.providers.NoProvider('Unable to find any recipe file matching "%s"' % pn) - return best[3] - - def get_file_appends(self, fn): - """ - Find the bbappends for a recipe file - """ - return self.run_command('getFileAppends', fn) - - def all_recipes(self, mc='', sort=True): - """ - Enable iterating over all recipes in the current configuration. - Returns an iterator over TinfoilRecipeInfo objects created on demand. - Parameters: - mc: The multiconfig, default of '' uses the main configuration. - sort: True to sort recipes alphabetically (default), False otherwise - """ - recipecache = self.cooker.recipecaches[mc] - if sort: - recipes = sorted(recipecache.pkg_pn.items()) - else: - recipes = recipecache.pkg_pn.items() - for pn, fns in recipes: - prov = self.find_best_provider(pn) - recipe = TinfoilRecipeInfo(recipecache, - self.config_data, - pn=pn, - fn=prov[3], - fns=fns) - yield recipe - - def all_recipe_files(self, mc='', variants=True, preferred_only=False): - """ - Enable iterating over all recipe files in the current configuration. - Returns an iterator over file paths. - Parameters: - mc: The multiconfig, default of '' uses the main configuration. - variants: True to include variants of recipes created through - BBCLASSEXTEND (default) or False to exclude them - preferred_only: True to include only the preferred recipe where - multiple exist providing the same PN, False to list - all recipes - """ - recipecache = self.cooker.recipecaches[mc] - if preferred_only: - files = [] - for pn in recipecache.pkg_pn.keys(): - prov = self.find_best_provider(pn) - files.append(prov[3]) - else: - files = recipecache.pkg_fn.keys() - for fn in sorted(files): - if not variants and fn.startswith('virtual:'): - continue - yield fn - - - def get_recipe_info(self, pn, mc=''): - """ - Get information on a specific recipe in the current configuration by name (PN). - Returns a TinfoilRecipeInfo object created on demand. - Parameters: - mc: The multiconfig, default of '' uses the main configuration. - """ - recipecache = self.cooker.recipecaches[mc] - prov = self.find_best_provider(pn) - fn = prov[3] - if fn: - actual_pn = recipecache.pkg_fn[fn] - recipe = TinfoilRecipeInfo(recipecache, - self.config_data, - pn=actual_pn, - fn=fn, - fns=recipecache.pkg_pn[actual_pn]) - return recipe - else: - return None - - def parse_recipe(self, pn): - """ - Parse the specified recipe and return a datastore object - representing the environment for the recipe. - """ - fn = self.get_recipe_file(pn) - return self.parse_recipe_file(fn) - - 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 self.tracking: - # Enable history tracking just for the parse operation - self.run_command('enableDataTracking') - try: - if appends and appendlist == []: - appends = False - if config_data: - dctr = bb.remotedata.RemoteDatastores.transmit_datastore(config_data) - dscon = self.run_command('parseRecipeFile', fn, appends, appendlist, dctr) - else: - dscon = self.run_command('parseRecipeFile', fn, appends, appendlist) - if dscon: - return self._reconvert_type(dscon, 'DataStoreConnectionHandle') - else: - return None - finally: - if self.tracking: - self.run_command('disableDataTracking') - - def build_file(self, buildfile, task, internal=True): - """ - Runs the specified task for just a single recipe (i.e. no dependencies). - This is equivalent to bitbake -b, except with the default internal=True - no warning about dependencies will be produced, normal info messages - from the runqueue will be silenced and BuildInit, BuildStarted and - BuildCompleted events will not be fired. - """ - return self.run_command('buildFile', buildfile, task, internal) - - def build_targets(self, targets, task=None, handle_events=True, extra_events=None, event_callback=None): - """ - Builds the specified targets. This is equivalent to a normal invocation - of bitbake. Has built-in event handling which is enabled by default and - can be extended if needed. - Parameters: - targets: - One or more targets to build. Can be a list or a - space-separated string. - task: - The task to run; if None then the value of BB_DEFAULT_TASK - will be used. Default None. - handle_events: - True to handle events in a similar way to normal bitbake - invocation with knotty; False to return immediately (on the - assumption that the caller will handle the events instead). - Default True. - extra_events: - An optional list of events to add to the event mask (if - handle_events=True). If you add events here you also need - to specify a callback function in event_callback that will - handle the additional events. Default None. - event_callback: - An optional function taking a single parameter which - will be called first upon receiving any event (if - handle_events=True) so that the caller can override or - extend the event handling. Default None. - """ - if isinstance(targets, str): - targets = targets.split() - if not task: - task = self.config_data.getVar('BB_DEFAULT_TASK') - - if handle_events: - # A reasonable set of default events matching up with those we handle below - eventmask = [ - 'bb.event.BuildStarted', - 'bb.event.BuildCompleted', - 'logging.LogRecord', - 'bb.event.NoProvider', - 'bb.command.CommandCompleted', - 'bb.command.CommandFailed', - 'bb.build.TaskStarted', - 'bb.build.TaskFailed', - 'bb.build.TaskSucceeded', - 'bb.build.TaskFailedSilent', - 'bb.build.TaskProgress', - 'bb.runqueue.runQueueTaskStarted', - 'bb.runqueue.sceneQueueTaskStarted', - 'bb.event.ProcessStarted', - 'bb.event.ProcessProgress', - 'bb.event.ProcessFinished', - ] - if extra_events: - eventmask.extend(extra_events) - ret = self.set_event_mask(eventmask) - - includelogs = self.config_data.getVar('BBINCLUDELOGS') - loglines = self.config_data.getVar('BBINCLUDELOGS_LINES') - - ret = self.run_command('buildTargets', targets, task) - if handle_events: - result = False - # Borrowed from knotty, instead somewhat hackily we use the helper - # as the object to store "shutdown" on - helper = bb.ui.uihelper.BBUIHelper() - # We set up logging optionally in the constructor so now we need to - # grab the handlers to pass to TerminalFilter - console = None - errconsole = None - for handler in self.logger.handlers: - if isinstance(handler, logging.StreamHandler): - if handler.stream == sys.stdout: - console = handler - elif handler.stream == sys.stderr: - errconsole = handler - format_str = "%(levelname)s: %(message)s" - format = bb.msg.BBLogFormatter(format_str) - helper.shutdown = 0 - parseprogress = None - termfilter = bb.ui.knotty.TerminalFilter(helper, helper, console, errconsole, format, quiet=self.quiet) - try: - while True: - try: - event = self.wait_event(0.25) - if event: - if event_callback and event_callback(event): - continue - if helper.eventHandler(event): - if isinstance(event, bb.build.TaskFailedSilent): - logger.warning("Logfile for failed setscene task is %s" % event.logfile) - elif isinstance(event, bb.build.TaskFailed): - bb.ui.knotty.print_event_log(event, includelogs, loglines, termfilter) - continue - if isinstance(event, bb.event.ProcessStarted): - if self.quiet > 1: - continue - parseprogress = bb.ui.knotty.new_progress(event.processname, event.total) - parseprogress.start(False) - continue - if isinstance(event, bb.event.ProcessProgress): - if self.quiet > 1: - continue - if parseprogress: - parseprogress.update(event.progress) - else: - bb.warn("Got ProcessProgress event for someting that never started?") - continue - if isinstance(event, bb.event.ProcessFinished): - if self.quiet > 1: - continue - if parseprogress: - parseprogress.finish() - parseprogress = None - continue - if isinstance(event, bb.command.CommandCompleted): - result = True - break - if isinstance(event, bb.command.CommandFailed): - self.logger.error(str(event)) - result = False - break - if isinstance(event, logging.LogRecord): - if event.taskpid == 0 or event.levelno > logging.INFO: - self.logger.handle(event) - continue - if isinstance(event, bb.event.NoProvider): - self.logger.error(str(event)) - result = False - break - - elif helper.shutdown > 1: - break - termfilter.updateFooter() - except KeyboardInterrupt: - termfilter.clearFooter() - if helper.shutdown == 1: - print("\nSecond Keyboard Interrupt, stopping...\n") - ret = self.run_command("stateForceShutdown") - if ret and ret[2]: - self.logger.error("Unable to cleanly stop: %s" % ret[2]) - elif helper.shutdown == 0: - print("\nKeyboard Interrupt, closing down...\n") - interrupted = True - ret = self.run_command("stateShutdown") - if ret and ret[2]: - self.logger.error("Unable to cleanly shutdown: %s" % ret[2]) - helper.shutdown = helper.shutdown + 1 - termfilter.clearFooter() - finally: - termfilter.finish() - if helper.failed_tasks: - result = False - return result - else: - return ret - - def shutdown(self): - """ - Shut down tinfoil. Disconnects from the server and gracefully - releases any associated resources. You must call this function if - prepare() has been called, or use a with... block when you create - the tinfoil object which will ensure that it gets called. - """ - if self.server_connection: - self.run_command('clientComplete') - _server_connections.remove(self.server_connection) - bb.event.ui_queue = [] - self.server_connection.terminate() - self.server_connection = None - - # Restore logging handlers to how it looked when we started - if self.oldhandlers: - for handler in self.logger.handlers: - if handler not in self.oldhandlers: - self.logger.handlers.remove(handler) - - def _reconvert_type(self, obj, origtypename): - """ - Convert an object back to the right type, in the case - that marshalling has changed it (especially with xmlrpc) - """ - supported_types = { - 'set': set, - 'DataStoreConnectionHandle': bb.command.DataStoreConnectionHandle, - } - - origtype = supported_types.get(origtypename, None) - if origtype is None: - raise Exception('Unsupported type "%s"' % origtypename) - if type(obj) == origtype: - newobj = obj - elif isinstance(obj, dict): - # New style class - newobj = origtype() - for k,v in obj.items(): - setattr(newobj, k, v) - else: - # Assume we can coerce the type - newobj = origtype(obj) - - if isinstance(newobj, bb.command.DataStoreConnectionHandle): - connector = TinfoilDataStoreConnector(self, newobj.dsindex) - newobj = bb.data.init() - newobj.setVar('_remote_data', connector) - - return newobj - - -class TinfoilConfigParameters(BitBakeConfigParameters): - - def __init__(self, config_only, **options): - self.initial_options = options - # Apply some sane defaults - if not 'parse_only' in options: - self.initial_options['parse_only'] = not config_only - #if not 'status_only' in options: - # self.initial_options['status_only'] = config_only - if not 'ui' in options: - self.initial_options['ui'] = 'knotty' - if not 'argv' in options: - self.initial_options['argv'] = [] - - super(TinfoilConfigParameters, self).__init__() - - def parseCommandLine(self, argv=None): - # We don't want any parameters parsed from the command line - opts = super(TinfoilConfigParameters, self).parseCommandLine([]) - for key, val in self.initial_options.items(): - setattr(opts[0], key, val) - return opts |