#!/usr/bin/env python # This script has subcommands which operate against your bitbake layers, either # displaying useful information, or acting against them. # Currently, it only provides a show_appends command, which shows you what # bbappends are in effect, and warns you if you have appends which are not being # utilized. import cmd import logging import os.path import sys bindir = os.path.dirname(__file__) topdir = os.path.dirname(bindir) sys.path[0:0] = [os.path.join(topdir, 'lib')] import bb.cache import bb.cooker import bb.providers import bb.utils from bb.cooker import state logger = logging.getLogger('BitBake') default_cmd = 'show_appends' def main(args): logging.basicConfig(format='%(levelname)s: %(message)s') bb.utils.clean_environment() cmds = Commands() if args: cmds.onecmd(' '.join(args)) else: cmds.onecmd(default_cmd) return cmds.returncode class Commands(cmd.Cmd): def __init__(self): cmd.Cmd.__init__(self) self.returncode = 0 self.config = Config(parse_only=True) self.cooker = bb.cooker.BBCooker(self.config, self.register_idle_function) self.config_data = self.cooker.configuration.data bb.providers.logger.setLevel(logging.ERROR) self.cooker_data = None def register_idle_function(self, function, data): pass def prepare_cooker(self): sys.stderr.write("Parsing recipes..") 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) logger.setLevel(logging.INFO) sys.stderr.write("done.\n") self.cooker_data = self.cooker.status self.cooker_data.appends = self.cooker.appendlist def check_prepare_cooker(self): if not self.cooker_data: self.prepare_cooker() def do_show_layers(self, args): self.check_prepare_cooker() logger.info(str(self.config_data.getVar('BBLAYERS', True))) def do_show_overlayed(self, args): self.check_prepare_cooker() if self.cooker.overlayed: logger.info('Overlayed recipes:') for f in self.cooker.overlayed.iterkeys(): logger.info('%s' % f) for of in self.cooker.overlayed[f]: logger.info(' %s' % of) else: logger.info('No overlayed recipes found') def do_flatten(self, args): arglist = args.split() if len(arglist) != 1: logger.error('syntax: flatten ') return if os.path.exists(arglist[0]) and os.listdir(arglist[0]): logger.error('Directory %s exists and is non-empty, please clear it out first' % arglist[0]) return self.check_prepare_cooker() layers = (self.config_data.getVar('BBLAYERS', True) or "").split() for layer in layers: overlayed = [] for f in self.cooker.overlayed.iterkeys(): for of in self.cooker.overlayed[f]: if of.startswith(layer): overlayed.append(of) logger.info('Copying files from %s...' % layer ) for root, dirs, files in os.walk(layer): for f1 in files: f1full = os.sep.join([root, f1]) if f1full in overlayed: logger.info(' Skipping overlayed file %s' % f1full ) else: ext = os.path.splitext(f1)[1] if ext != '.bbappend': fdest = f1full[len(layer):] fdest = os.path.normpath(os.sep.join([arglist[0],fdest])) bb.utils.mkdirhier(os.path.dirname(fdest)) if os.path.exists(fdest): if f1 == 'layer.conf' and root.endswith('/conf'): logger.info(' Skipping layer config file %s' % f1full ) continue else: logger.warn('Overwriting file %s', fdest) bb.utils.copyfile(f1full, fdest) if ext == '.bb': if f1 in self.cooker_data.appends: appends = self.cooker_data.appends[f1] if appends: logger.info(' Applying appends to %s' % fdest ) for appendname in appends: self.apply_append(appendname, fdest) def get_append_layer(self, appendname): for layer, _, regex, _ in self.cooker.status.bbfile_config_priorities: if regex.match(appendname): return layer return "?" def apply_append(self, appendname, recipename): appendfile = open(appendname, 'r') recipefile = open(recipename, 'a') recipefile.write('\n') recipefile.write('##### bbappended from %s #####\n' % self.get_append_layer(appendname)) recipefile.writelines(appendfile.readlines()) def do_show_appends(self, args): self.check_prepare_cooker() if not self.cooker_data.appends: logger.info('No append files found') return logger.info('State of append files:') pnlist = list(self.cooker_data.pkg_pn.keys()) pnlist.sort() for pn in pnlist: self.show_appends_for_pn(pn) self.show_appends_for_skipped() def show_appends_for_pn(self, pn): filenames = self.cooker_data.pkg_pn[pn] best = bb.providers.findBestProvider(pn, self.cooker.configuration.data, self.cooker_data, self.cooker_data.pkg_pn) best_filename = os.path.basename(best[3]) self.show_appends_output(filenames, best_filename) def show_appends_for_skipped(self): filenames = [os.path.basename(f) for f in self.cooker.skiplist.iterkeys()] self.show_appends_output(filenames, None, " (skipped)") def show_appends_output(self, filenames, best_filename, name_suffix = ''): appended, missing = self.get_appends_for_files(filenames) if appended: for basename, appends in appended: logger.info('%s%s:', basename, name_suffix) for append in appends: logger.info(' %s', append) if best_filename: if best_filename in missing: logger.warn('%s: missing append for preferred version', best_filename) self.returncode |= 1 def get_appends_for_files(self, filenames): appended, notappended = [], [] for filename in filenames: _, cls = bb.cache.Cache.virtualfn2realfn(filename) if cls: continue basename = os.path.basename(filename) appends = self.cooker_data.appends.get(basename) if appends: appended.append((basename, list(appends))) else: notappended.append(basename) return appended, notappended def do_EOF(self, line): return True class Config(object): def __init__(self, **options): self.pkgs_to_build = [] self.debug_domains = [] self.extra_assume_provided = [] self.prefile = [] self.postfile = [] self.debug = 0 self.__dict__.update(options) def __getattr__(self, attribute): try: return super(Config, self).__getattribute__(attribute) except AttributeError: return None if __name__ == '__main__': sys.exit(main(sys.argv[1:]) or 0)