summaryrefslogtreecommitdiffstats
path: root/import-layers/yocto-poky/bitbake/bin/bitbake-layers
diff options
context:
space:
mode:
Diffstat (limited to 'import-layers/yocto-poky/bitbake/bin/bitbake-layers')
-rwxr-xr-ximport-layers/yocto-poky/bitbake/bin/bitbake-layers1072
1 files changed, 1072 insertions, 0 deletions
diff --git a/import-layers/yocto-poky/bitbake/bin/bitbake-layers b/import-layers/yocto-poky/bitbake/bin/bitbake-layers
new file mode 100755
index 000000000..d47a6690e
--- /dev/null
+++ b/import-layers/yocto-poky/bitbake/bin/bitbake-layers
@@ -0,0 +1,1072 @@
+#!/usr/bin/env python
+
+# This script has subcommands which operate against your bitbake layers, either
+# displaying useful information, or acting against them.
+# See the help output for details on available commands.
+
+# Copyright (C) 2011 Mentor Graphics Corporation
+# Copyright (C) 2011-2015 Intel Corporation
+#
+# 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 fnmatch
+from collections import defaultdict
+import argparse
+import re
+import httplib, urlparse, json
+import subprocess
+
+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
+import bb.tinfoil
+
+
+def logger_create(name, output=sys.stderr):
+ logger = logging.getLogger(name)
+ console = logging.StreamHandler(output)
+ format = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
+ if output.isatty():
+ format.enable_color()
+ console.setFormatter(format)
+ logger.addHandler(console)
+ logger.setLevel(logging.INFO)
+ return logger
+
+logger = logger_create('bitbake-layers', sys.stdout)
+
+class UserError(Exception):
+ pass
+
+class Commands():
+ def __init__(self):
+ self.bbhandler = None
+ self.bblayers = []
+
+ def init_bbhandler(self, config_only = False):
+ if not self.bbhandler:
+ self.bbhandler = bb.tinfoil.Tinfoil(tracking=True)
+ self.bblayers = (self.bbhandler.config_data.getVar('BBLAYERS', True) or "").split()
+ self.bbhandler.prepare(config_only)
+ layerconfs = self.bbhandler.config_data.varhistory.get_variable_items_files('BBFILE_COLLECTIONS', self.bbhandler.config_data)
+ self.bbfile_collections = {layer: os.path.dirname(os.path.dirname(path)) for layer, path in layerconfs.iteritems()}
+
+
+ def do_show_layers(self, args):
+ """show current configured layers"""
+ self.init_bbhandler(config_only = True)
+ logger.plain("%s %s %s" % ("layer".ljust(20), "path".ljust(40), "priority"))
+ logger.plain('=' * 74)
+ for layer, _, regex, pri in self.bbhandler.cooker.recipecache.bbfile_config_priorities:
+ layerdir = self.bbfile_collections.get(layer, None)
+ layername = self.get_layer_name(layerdir)
+ logger.plain("%s %s %d" % (layername.ljust(20), layerdir.ljust(40), pri))
+
+
+ def do_add_layer(self, args):
+ """Add a layer to bblayers.conf
+
+Adds the specified layer to bblayers.conf
+"""
+ layerdir = os.path.abspath(args.layerdir)
+ if not os.path.exists(layerdir):
+ sys.stderr.write("Specified layer directory doesn't exist\n")
+ return 1
+
+ layer_conf = os.path.join(layerdir, 'conf', 'layer.conf')
+ if not os.path.exists(layer_conf):
+ sys.stderr.write("Specified layer directory doesn't contain a conf/layer.conf file\n")
+ return 1
+
+ bblayers_conf = os.path.join('conf', 'bblayers.conf')
+ if not os.path.exists(bblayers_conf):
+ sys.stderr.write("Unable to find bblayers.conf\n")
+ return 1
+
+ (notadded, _) = bb.utils.edit_bblayers_conf(bblayers_conf, layerdir, None)
+ if notadded:
+ for item in notadded:
+ sys.stderr.write("Specified layer %s is already in BBLAYERS\n" % item)
+
+
+ def do_remove_layer(self, args):
+ """Remove a layer from bblayers.conf
+
+Removes the specified layer from bblayers.conf
+"""
+ bblayers_conf = os.path.join('conf', 'bblayers.conf')
+ if not os.path.exists(bblayers_conf):
+ sys.stderr.write("Unable to find bblayers.conf\n")
+ return 1
+
+ if args.layerdir.startswith('*'):
+ layerdir = args.layerdir
+ elif not '/' in args.layerdir:
+ layerdir = '*/%s' % args.layerdir
+ else:
+ layerdir = os.path.abspath(args.layerdir)
+ (_, notremoved) = bb.utils.edit_bblayers_conf(bblayers_conf, None, layerdir)
+ if notremoved:
+ for item in notremoved:
+ sys.stderr.write("No layers matching %s found in BBLAYERS\n" % item)
+ return 1
+
+
+ def get_json_data(self, apiurl):
+ proxy_settings = os.environ.get("http_proxy", None)
+ conn = None
+ _parsedurl = urlparse.urlparse(apiurl)
+ path = _parsedurl.path
+ query = _parsedurl.query
+ def parse_url(url):
+ parsedurl = urlparse.urlparse(url)
+ if parsedurl.netloc[0] == '[':
+ host, port = parsedurl.netloc[1:].split(']', 1)
+ if ':' in port:
+ port = port.rsplit(':', 1)[1]
+ else:
+ port = None
+ else:
+ if parsedurl.netloc.count(':') == 1:
+ (host, port) = parsedurl.netloc.split(":")
+ else:
+ host = parsedurl.netloc
+ port = None
+ return (host, 80 if port is None else int(port))
+
+ if proxy_settings is None:
+ host, port = parse_url(apiurl)
+ conn = httplib.HTTPConnection(host, port)
+ conn.request("GET", path + "?" + query)
+ else:
+ host, port = parse_url(proxy_settings)
+ conn = httplib.HTTPConnection(host, port)
+ conn.request("GET", apiurl)
+
+ r = conn.getresponse()
+ if r.status != 200:
+ raise Exception("Failed to read " + path + ": %d %s" % (r.status, r.reason))
+ return json.loads(r.read())
+
+
+ def get_layer_deps(self, layername, layeritems, layerbranches, layerdependencies, branchnum, selfname=False):
+ def layeritems_info_id(items_name, layeritems):
+ litems_id = None
+ for li in layeritems:
+ if li['name'] == items_name:
+ litems_id = li['id']
+ break
+ return litems_id
+
+ def layerbranches_info(items_id, layerbranches):
+ lbranch = {}
+ for lb in layerbranches:
+ if lb['layer'] == items_id and lb['branch'] == branchnum:
+ lbranch['id'] = lb['id']
+ lbranch['vcs_subdir'] = lb['vcs_subdir']
+ break
+ return lbranch
+
+ def layerdependencies_info(lb_id, layerdependencies):
+ ld_deps = []
+ for ld in layerdependencies:
+ if ld['layerbranch'] == lb_id and not ld['dependency'] in ld_deps:
+ ld_deps.append(ld['dependency'])
+ if not ld_deps:
+ logger.error("The dependency of layerDependencies is not found.")
+ return ld_deps
+
+ def layeritems_info_name_subdir(items_id, layeritems):
+ litems = {}
+ for li in layeritems:
+ if li['id'] == items_id:
+ litems['vcs_url'] = li['vcs_url']
+ litems['name'] = li['name']
+ break
+ return litems
+
+ if selfname:
+ selfid = layeritems_info_id(layername, layeritems)
+ lbinfo = layerbranches_info(selfid, layerbranches)
+ if lbinfo:
+ selfsubdir = lbinfo['vcs_subdir']
+ else:
+ logger.error("%s is not found in the specified branch" % layername)
+ return
+ selfurl = layeritems_info_name_subdir(selfid, layeritems)['vcs_url']
+ if selfurl:
+ return selfurl, selfsubdir
+ else:
+ logger.error("Cannot get layer %s git repo and subdir" % layername)
+ return
+ ldict = {}
+ itemsid = layeritems_info_id(layername, layeritems)
+ if not itemsid:
+ return layername, None
+ lbid = layerbranches_info(itemsid, layerbranches)
+ if lbid:
+ lbid = layerbranches_info(itemsid, layerbranches)['id']
+ else:
+ logger.error("%s is not found in the specified branch" % layername)
+ return None, None
+ for dependency in layerdependencies_info(lbid, layerdependencies):
+ lname = layeritems_info_name_subdir(dependency, layeritems)['name']
+ lurl = layeritems_info_name_subdir(dependency, layeritems)['vcs_url']
+ lsubdir = layerbranches_info(dependency, layerbranches)['vcs_subdir']
+ ldict[lname] = lurl, lsubdir
+ return None, ldict
+
+
+ def get_fetch_layer(self, fetchdir, url, subdir, fetch_layer):
+ layername = self.get_layer_name(url)
+ if os.path.splitext(layername)[1] == '.git':
+ layername = os.path.splitext(layername)[0]
+ repodir = os.path.join(fetchdir, layername)
+ layerdir = os.path.join(repodir, subdir)
+ if not os.path.exists(repodir):
+ if fetch_layer:
+ result = subprocess.call('git clone %s %s' % (url, repodir), shell = True)
+ if result:
+ logger.error("Failed to download %s" % url)
+ return None, None
+ else:
+ return layername, layerdir
+ else:
+ logger.plain("Repository %s needs to be fetched" % url)
+ return layername, layerdir
+ elif os.path.exists(layerdir):
+ return layername, layerdir
+ else:
+ logger.error("%s is not in %s" % (url, subdir))
+ return None, None
+
+
+ def do_layerindex_fetch(self, args):
+ """Fetches a layer from a layer index along with its dependent layers, and adds them to conf/bblayers.conf.
+"""
+ self.init_bbhandler(config_only = True)
+ apiurl = self.bbhandler.config_data.getVar('BBLAYERS_LAYERINDEX_URL', True)
+ if not apiurl:
+ logger.error("Cannot get BBLAYERS_LAYERINDEX_URL")
+ return 1
+ else:
+ if apiurl[-1] != '/':
+ apiurl += '/'
+ apiurl += "api/"
+ apilinks = self.get_json_data(apiurl)
+ branches = self.get_json_data(apilinks['branches'])
+
+ branchnum = 0
+ for branch in branches:
+ if branch['name'] == args.branch:
+ branchnum = branch['id']
+ break
+ if branchnum == 0:
+ validbranches = ', '.join([branch['name'] for branch in branches])
+ logger.error('Invalid layer branch name "%s". Valid branches: %s' % (args.branch, validbranches))
+ return 1
+
+ ignore_layers = []
+ for collection in self.bbhandler.config_data.getVar('BBFILE_COLLECTIONS', True).split():
+ lname = self.bbhandler.config_data.getVar('BBLAYERS_LAYERINDEX_NAME_%s' % collection, True)
+ if lname:
+ ignore_layers.append(lname)
+
+ if args.ignore:
+ ignore_layers.extend(args.ignore.split(','))
+
+ layeritems = self.get_json_data(apilinks['layerItems'])
+ layerbranches = self.get_json_data(apilinks['layerBranches'])
+ layerdependencies = self.get_json_data(apilinks['layerDependencies'])
+ invaluenames = []
+ repourls = {}
+ printlayers = []
+ def query_dependencies(layers, layeritems, layerbranches, layerdependencies, branchnum):
+ depslayer = []
+ for layername in layers:
+ invaluename, layerdict = self.get_layer_deps(layername, layeritems, layerbranches, layerdependencies, branchnum)
+ if layerdict:
+ repourls[layername] = self.get_layer_deps(layername, layeritems, layerbranches, layerdependencies, branchnum, selfname=True)
+ for layer in layerdict:
+ if not layer in ignore_layers:
+ depslayer.append(layer)
+ printlayers.append((layername, layer, layerdict[layer][0], layerdict[layer][1]))
+ if not layer in ignore_layers and not layer in repourls:
+ repourls[layer] = (layerdict[layer][0], layerdict[layer][1])
+ if invaluename and not invaluename in invaluenames:
+ invaluenames.append(invaluename)
+ return depslayer
+
+ depslayers = query_dependencies(args.layername, layeritems, layerbranches, layerdependencies, branchnum)
+ while depslayers:
+ depslayer = query_dependencies(depslayers, layeritems, layerbranches, layerdependencies, branchnum)
+ depslayers = depslayer
+ if invaluenames:
+ for invaluename in invaluenames:
+ logger.error('Layer "%s" not found in layer index' % invaluename)
+ return 1
+ logger.plain("%s %s %s %s" % ("Layer".ljust(19), "Required by".ljust(19), "Git repository".ljust(54), "Subdirectory"))
+ logger.plain('=' * 115)
+ for layername in args.layername:
+ layerurl = repourls[layername]
+ logger.plain("%s %s %s %s" % (layername.ljust(20), '-'.ljust(20), layerurl[0].ljust(55), layerurl[1]))
+ printedlayers = []
+ for layer, dependency, gitrepo, subdirectory in printlayers:
+ if dependency in printedlayers:
+ continue
+ logger.plain("%s %s %s %s" % (dependency.ljust(20), layer.ljust(20), gitrepo.ljust(55), subdirectory))
+ printedlayers.append(dependency)
+
+ if repourls:
+ fetchdir = self.bbhandler.config_data.getVar('BBLAYERS_FETCH_DIR', True)
+ if not fetchdir:
+ logger.error("Cannot get BBLAYERS_FETCH_DIR")
+ return 1
+ if not os.path.exists(fetchdir):
+ os.makedirs(fetchdir)
+ addlayers = []
+ for repourl, subdir in repourls.values():
+ name, layerdir = self.get_fetch_layer(fetchdir, repourl, subdir, not args.show_only)
+ if not name:
+ # Error already shown
+ return 1
+ addlayers.append((subdir, name, layerdir))
+ if not args.show_only:
+ for subdir, name, layerdir in set(addlayers):
+ if os.path.exists(layerdir):
+ if subdir:
+ logger.plain("Adding layer \"%s\" to conf/bblayers.conf" % subdir)
+ else:
+ logger.plain("Adding layer \"%s\" to conf/bblayers.conf" % name)
+ localargs = argparse.Namespace()
+ localargs.layerdir = layerdir
+ self.do_add_layer(localargs)
+ else:
+ break
+
+
+ def do_layerindex_show_depends(self, args):
+ """Find layer dependencies from layer index.
+"""
+ args.show_only = True
+ args.ignore = []
+ self.do_layerindex_fetch(args)
+
+
+ def version_str(self, pe, pv, pr = None):
+ verstr = "%s" % pv
+ if pr:
+ verstr = "%s-%s" % (verstr, pr)
+ if pe:
+ verstr = "%s:%s" % (pe, verstr)
+ return verstr
+
+
+ def do_show_overlayed(self, args):
+ """list overlayed recipes (where the same recipe exists in another layer)
+
+Lists the names of overlayed recipes and the available versions in each
+layer, with the preferred version first. Note that skipped recipes that
+are overlayed will also be listed, with a " (skipped)" suffix.
+"""
+ self.init_bbhandler()
+
+ items_listed = self.list_recipes('Overlayed recipes', None, True, args.same_version, args.filenames, True, None)
+
+ # Check for overlayed .bbclass files
+ classes = defaultdict(list)
+ for layerdir in self.bblayers:
+ classdir = os.path.join(layerdir, 'classes')
+ if os.path.exists(classdir):
+ for classfile in os.listdir(classdir):
+ if os.path.splitext(classfile)[1] == '.bbclass':
+ classes[classfile].append(classdir)
+
+ # Locating classes and other files is a bit more complicated than recipes -
+ # layer priority is not a factor; instead BitBake uses the first matching
+ # file in BBPATH, which is manipulated directly by each layer's
+ # conf/layer.conf in turn, thus the order of layers in bblayers.conf is a
+ # factor - however, each layer.conf is free to either prepend or append to
+ # BBPATH (or indeed do crazy stuff with it). Thus the order in BBPATH might
+ # not be exactly the order present in bblayers.conf either.
+ bbpath = str(self.bbhandler.config_data.getVar('BBPATH', True))
+ overlayed_class_found = False
+ for (classfile, classdirs) in classes.items():
+ if len(classdirs) > 1:
+ if not overlayed_class_found:
+ logger.plain('=== Overlayed classes ===')
+ overlayed_class_found = True
+
+ mainfile = bb.utils.which(bbpath, os.path.join('classes', classfile))
+ if args.filenames:
+ logger.plain('%s' % mainfile)
+ else:
+ # We effectively have to guess the layer here
+ logger.plain('%s:' % classfile)
+ mainlayername = '?'
+ for layerdir in self.bblayers:
+ classdir = os.path.join(layerdir, 'classes')
+ if mainfile.startswith(classdir):
+ mainlayername = self.get_layer_name(layerdir)
+ logger.plain(' %s' % mainlayername)
+ for classdir in classdirs:
+ fullpath = os.path.join(classdir, classfile)
+ if fullpath != mainfile:
+ if args.filenames:
+ print(' %s' % fullpath)
+ else:
+ print(' %s' % self.get_layer_name(os.path.dirname(classdir)))
+
+ if overlayed_class_found:
+ items_listed = True;
+
+ if not items_listed:
+ logger.plain('No overlayed files found.')
+
+
+ def do_show_recipes(self, args):
+ """list available recipes, showing the layer they are provided by
+
+Lists the names of recipes and the available versions in each
+layer, with the preferred version first. Optionally you may specify
+pnspec to match a specified recipe name (supports wildcards). Note that
+skipped recipes will also be listed, with a " (skipped)" suffix.
+"""
+ self.init_bbhandler()
+
+ inheritlist = args.inherits.split(',') if args.inherits else []
+ if inheritlist or args.pnspec or args.multiple:
+ title = 'Matching recipes:'
+ else:
+ title = 'Available recipes:'
+ self.list_recipes(title, args.pnspec, False, False, args.filenames, args.multiple, inheritlist)
+
+
+ def list_recipes(self, title, pnspec, show_overlayed_only, show_same_ver_only, show_filenames, show_multi_provider_only, inherits):
+ if inherits:
+ bbpath = str(self.bbhandler.config_data.getVar('BBPATH', True))
+ for classname in inherits:
+ classfile = 'classes/%s.bbclass' % classname
+ if not bb.utils.which(bbpath, classfile, history=False):
+ raise UserError('No class named %s found in BBPATH' % classfile)
+
+ pkg_pn = self.bbhandler.cooker.recipecache.pkg_pn
+ (latest_versions, preferred_versions) = bb.providers.findProviders(self.bbhandler.config_data, self.bbhandler.cooker.recipecache, pkg_pn)
+ allproviders = bb.providers.allProviders(self.bbhandler.cooker.recipecache)
+
+ # Ensure we list skipped recipes
+ # We are largely guessing about PN, PV and the preferred version here,
+ # but we have no choice since skipped recipes are not fully parsed
+ skiplist = self.bbhandler.cooker.skiplist.keys()
+ skiplist.sort( key=lambda fileitem: self.bbhandler.cooker.collection.calc_bbfile_priority(fileitem) )
+ skiplist.reverse()
+ for fn in skiplist:
+ recipe_parts = os.path.splitext(os.path.basename(fn))[0].split('_')
+ p = recipe_parts[0]
+ if len(recipe_parts) > 1:
+ ver = (None, recipe_parts[1], None)
+ else:
+ ver = (None, 'unknown', None)
+ allproviders[p].append((ver, fn))
+ if not p in pkg_pn:
+ pkg_pn[p] = 'dummy'
+ preferred_versions[p] = (ver, fn)
+
+ def print_item(f, pn, ver, layer, ispref):
+ if f in skiplist:
+ skipped = ' (skipped)'
+ else:
+ skipped = ''
+ if show_filenames:
+ if ispref:
+ logger.plain("%s%s", f, skipped)
+ else:
+ logger.plain(" %s%s", f, skipped)
+ else:
+ if ispref:
+ logger.plain("%s:", pn)
+ logger.plain(" %s %s%s", layer.ljust(20), ver, skipped)
+
+ global_inherit = (self.bbhandler.config_data.getVar('INHERIT', True) or "").split()
+ cls_re = re.compile('classes/')
+
+ preffiles = []
+ items_listed = False
+ for p in sorted(pkg_pn):
+ if pnspec:
+ if not fnmatch.fnmatch(p, pnspec):
+ continue
+
+ if len(allproviders[p]) > 1 or not show_multi_provider_only:
+ pref = preferred_versions[p]
+ realfn = bb.cache.Cache.virtualfn2realfn(pref[1])
+ preffile = realfn[0]
+
+ # We only display once per recipe, we should prefer non extended versions of the
+ # recipe if present (so e.g. in OpenEmbedded, openssl rather than nativesdk-openssl
+ # which would otherwise sort first).
+ if realfn[1] and realfn[0] in self.bbhandler.cooker.recipecache.pkg_fn:
+ continue
+
+ if inherits:
+ matchcount = 0
+ recipe_inherits = self.bbhandler.cooker_data.inherits.get(preffile, [])
+ for cls in recipe_inherits:
+ if cls_re.match(cls):
+ continue
+ classname = os.path.splitext(os.path.basename(cls))[0]
+ if classname in global_inherit:
+ continue
+ elif classname in inherits:
+ matchcount += 1
+ if matchcount != len(inherits):
+ # No match - skip this recipe
+ continue
+
+ if preffile not in preffiles:
+ preflayer = self.get_file_layer(preffile)
+ multilayer = False
+ same_ver = True
+ provs = []
+ for prov in allproviders[p]:
+ provfile = bb.cache.Cache.virtualfn2realfn(prov[1])[0]
+ provlayer = self.get_file_layer(provfile)
+ provs.append((provfile, provlayer, prov[0]))
+ if provlayer != preflayer:
+ multilayer = True
+ if prov[0] != pref[0]:
+ same_ver = False
+
+ if (multilayer or not show_overlayed_only) and (same_ver or not show_same_ver_only):
+ if not items_listed:
+ logger.plain('=== %s ===' % title)
+ items_listed = True
+ print_item(preffile, p, self.version_str(pref[0][0], pref[0][1]), preflayer, True)
+ for (provfile, provlayer, provver) in provs:
+ if provfile != preffile:
+ print_item(provfile, p, self.version_str(provver[0], provver[1]), provlayer, False)
+ # Ensure we don't show two entries for BBCLASSEXTENDed recipes
+ preffiles.append(preffile)
+
+ return items_listed
+
+
+ def do_flatten(self, args):
+ """flatten layer configuration into a separate output directory.
+
+Takes the specified layers (or all layers in the current layer
+configuration if none are specified) and builds a "flattened" directory
+containing the contents of all layers, with any overlayed recipes removed
+and bbappends appended to the corresponding recipes. Note that some manual
+cleanup may still be necessary afterwards, in particular:
+
+* where non-recipe files (such as patches) are overwritten (the flatten
+ command will show a warning for these)
+* where anything beyond the normal layer setup has been added to
+ layer.conf (only the lowest priority number layer's layer.conf is used)
+* overridden/appended items from bbappends will need to be tidied up
+* when the flattened layers do not have the same directory structure (the
+ flatten command should show a warning when this will cause a problem)
+
+Warning: if you flatten several layers where another layer is intended to
+be used "inbetween" them (in layer priority order) such that recipes /
+bbappends in the layers interact, and then attempt to use the new output
+layer together with that other layer, you may no longer get the same
+build results (as the layer priority order has effectively changed).
+"""
+ if len(args.layer) == 1:
+ logger.error('If you specify layers to flatten you must specify at least two')
+ return 1
+
+ outputdir = args.outputdir
+ if os.path.exists(outputdir) and os.listdir(outputdir):
+ logger.error('Directory %s exists and is non-empty, please clear it out first' % outputdir)
+ return 1
+
+ self.init_bbhandler()
+ layers = self.bblayers
+ if len(args.layer) > 2:
+ layernames = args.layer
+ found_layernames = []
+ found_layerdirs = []
+ for layerdir in layers:
+ layername = self.get_layer_name(layerdir)
+ if layername in layernames:
+ found_layerdirs.append(layerdir)
+ found_layernames.append(layername)
+
+ for layername in layernames:
+ if not layername in found_layernames:
+ logger.error('Unable to find layer %s in current configuration, please run "%s show-layers" to list configured layers' % (layername, os.path.basename(sys.argv[0])))
+ return
+ layers = found_layerdirs
+ else:
+ layernames = []
+
+ # Ensure a specified path matches our list of layers
+ def layer_path_match(path):
+ for layerdir in layers:
+ if path.startswith(os.path.join(layerdir, '')):
+ return layerdir
+ return None
+
+ applied_appends = []
+ for layer in layers:
+ overlayed = []
+ for f in self.bbhandler.cooker.collection.overlayed.iterkeys():
+ for of in self.bbhandler.cooker.collection.overlayed[f]:
+ if of.startswith(layer):
+ overlayed.append(of)
+
+ logger.plain('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.plain(' 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([outputdir,fdest]))
+ bb.utils.mkdirhier(os.path.dirname(fdest))
+ if os.path.exists(fdest):
+ if f1 == 'layer.conf' and root.endswith('/conf'):
+ logger.plain(' Skipping layer config file %s' % f1full )
+ continue
+ else:
+ logger.warn('Overwriting file %s', fdest)
+ bb.utils.copyfile(f1full, fdest)
+ if ext == '.bb':
+ for append in self.bbhandler.cooker.collection.get_file_appends(f1full):
+ if layer_path_match(append):
+ logger.plain(' Applying append %s to %s' % (append, fdest))
+ self.apply_append(append, fdest)
+ applied_appends.append(append)
+
+ # Take care of when some layers are excluded and yet we have included bbappends for those recipes
+ for b in self.bbhandler.cooker.collection.bbappends:
+ (recipename, appendname) = b
+ if appendname not in applied_appends:
+ first_append = None
+ layer = layer_path_match(appendname)
+ if layer:
+ if first_append:
+ self.apply_append(appendname, first_append)
+ else:
+ fdest = appendname[len(layer):]
+ fdest = os.path.normpath(os.sep.join([outputdir,fdest]))
+ bb.utils.mkdirhier(os.path.dirname(fdest))
+ bb.utils.copyfile(appendname, fdest)
+ first_append = fdest
+
+ # Get the regex for the first layer in our list (which is where the conf/layer.conf file will
+ # have come from)
+ first_regex = None
+ layerdir = layers[0]
+ for layername, pattern, regex, _ in self.bbhandler.cooker.recipecache.bbfile_config_priorities:
+ if regex.match(os.path.join(layerdir, 'test')):
+ first_regex = regex
+ break
+
+ if first_regex:
+ # Find the BBFILES entries that match (which will have come from this conf/layer.conf file)
+ bbfiles = str(self.bbhandler.config_data.getVar('BBFILES', True)).split()
+ bbfiles_layer = []
+ for item in bbfiles:
+ if first_regex.match(item):
+ newpath = os.path.join(outputdir, item[len(layerdir)+1:])
+ bbfiles_layer.append(newpath)
+
+ if bbfiles_layer:
+ # Check that all important layer files match BBFILES
+ for root, dirs, files in os.walk(outputdir):
+ for f1 in files:
+ ext = os.path.splitext(f1)[1]
+ if ext in ['.bb', '.bbappend']:
+ f1full = os.sep.join([root, f1])
+ entry_found = False
+ for item in bbfiles_layer:
+ if fnmatch.fnmatch(f1full, item):
+ entry_found = True
+ break
+ if not entry_found:
+ logger.warning("File %s does not match the flattened layer's BBFILES setting, you may need to edit conf/layer.conf or move the file elsewhere" % f1full)
+
+ def get_file_layer(self, filename):
+ layerdir = self.get_file_layerdir(filename)
+ if layerdir:
+ return self.get_layer_name(layerdir)
+ else:
+ return '?'
+
+ def get_file_layerdir(self, filename):
+ layer = bb.utils.get_file_layer(filename, self.bbhandler.config_data)
+ return self.bbfile_collections.get(layer, None)
+
+ def remove_layer_prefix(self, f):
+ """Remove the layer_dir prefix, e.g., f = /path/to/layer_dir/foo/blah, the
+ return value will be: layer_dir/foo/blah"""
+ f_layerdir = self.get_file_layerdir(f)
+ if not f_layerdir:
+ return f
+ prefix = os.path.join(os.path.dirname(f_layerdir), '')
+ return f[len(prefix):] if f.startswith(prefix) else f
+
+ def get_layer_name(self, layerdir):
+ return os.path.basename(layerdir.rstrip(os.sep))
+
+ def apply_append(self, appendname, recipename):
+ with open(appendname, 'r') as appendfile:
+ with open(recipename, 'a') as recipefile:
+ recipefile.write('\n')
+ recipefile.write('##### bbappended from %s #####\n' % self.get_file_layer(appendname))
+ recipefile.writelines(appendfile.readlines())
+
+ def do_show_appends(self, args):
+ """list bbappend files and recipe files they apply to
+
+Lists recipes with the bbappends that apply to them as subitems.
+"""
+ self.init_bbhandler()
+
+ logger.plain('=== Appended recipes ===')
+
+ pnlist = list(self.bbhandler.cooker_data.pkg_pn.keys())
+ pnlist.sort()
+ appends = False
+ for pn in pnlist:
+ if self.show_appends_for_pn(pn):
+ appends = True
+
+ if self.show_appends_for_skipped():
+ appends = True
+
+ if not appends:
+ logger.plain('No append files found')
+
+ def show_appends_for_pn(self, pn):
+ filenames = self.bbhandler.cooker_data.pkg_pn[pn]
+
+ best = bb.providers.findBestProvider(pn,
+ self.bbhandler.config_data,
+ self.bbhandler.cooker_data,
+ self.bbhandler.cooker_data.pkg_pn)
+ best_filename = os.path.basename(best[3])
+
+ return self.show_appends_output(filenames, best_filename)
+
+ def show_appends_for_skipped(self):
+ filenames = [os.path.basename(f)
+ for f in self.bbhandler.cooker.skiplist.iterkeys()]
+ return 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.plain('%s%s:', basename, name_suffix)
+ for append in appends:
+ logger.plain(' %s', append)
+
+ if best_filename:
+ if best_filename in missing:
+ logger.warn('%s: missing append for preferred version',
+ best_filename)
+ return True
+ else:
+ return False
+
+ 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.bbhandler.cooker.collection.get_file_appends(basename)
+ if appends:
+ appended.append((basename, list(appends)))
+ else:
+ notappended.append(basename)
+ return appended, notappended
+
+ def do_show_cross_depends(self, args):
+ """Show dependencies between recipes that cross layer boundaries.
+
+Figure out the dependencies between recipes that cross layer boundaries.
+
+NOTE: .bbappend files can impact the dependencies.
+"""
+ ignore_layers = (args.ignore or '').split(',')
+
+ self.init_bbhandler()
+
+ pkg_fn = self.bbhandler.cooker_data.pkg_fn
+ bbpath = str(self.bbhandler.config_data.getVar('BBPATH', True))
+ self.require_re = re.compile(r"require\s+(.+)")
+ self.include_re = re.compile(r"include\s+(.+)")
+ self.inherit_re = re.compile(r"inherit\s+(.+)")
+
+ global_inherit = (self.bbhandler.config_data.getVar('INHERIT', True) or "").split()
+
+ # The bb's DEPENDS and RDEPENDS
+ for f in pkg_fn:
+ f = bb.cache.Cache.virtualfn2realfn(f)[0]
+ # Get the layername that the file is in
+ layername = self.get_file_layer(f)
+
+ # The DEPENDS
+ deps = self.bbhandler.cooker_data.deps[f]
+ for pn in deps:
+ if pn in self.bbhandler.cooker_data.pkg_pn:
+ best = bb.providers.findBestProvider(pn,
+ self.bbhandler.config_data,
+ self.bbhandler.cooker_data,
+ self.bbhandler.cooker_data.pkg_pn)
+ self.check_cross_depends("DEPENDS", layername, f, best[3], args.filenames, ignore_layers)
+
+ # The RDPENDS
+ all_rdeps = self.bbhandler.cooker_data.rundeps[f].values()
+ # Remove the duplicated or null one.
+ sorted_rdeps = {}
+ # The all_rdeps is the list in list, so we need two for loops
+ for k1 in all_rdeps:
+ for k2 in k1:
+ sorted_rdeps[k2] = 1
+ all_rdeps = sorted_rdeps.keys()
+ for rdep in all_rdeps:
+ all_p = bb.providers.getRuntimeProviders(self.bbhandler.cooker_data, rdep)
+ if all_p:
+ if f in all_p:
+ # The recipe provides this one itself, ignore
+ continue
+ best = bb.providers.filterProvidersRunTime(all_p, rdep,
+ self.bbhandler.config_data,
+ self.bbhandler.cooker_data)[0][0]
+ self.check_cross_depends("RDEPENDS", layername, f, best, args.filenames, ignore_layers)
+
+ # The RRECOMMENDS
+ all_rrecs = self.bbhandler.cooker_data.runrecs[f].values()
+ # Remove the duplicated or null one.
+ sorted_rrecs = {}
+ # The all_rrecs is the list in list, so we need two for loops
+ for k1 in all_rrecs:
+ for k2 in k1:
+ sorted_rrecs[k2] = 1
+ all_rrecs = sorted_rrecs.keys()
+ for rrec in all_rrecs:
+ all_p = bb.providers.getRuntimeProviders(self.bbhandler.cooker_data, rrec)
+ if all_p:
+ if f in all_p:
+ # The recipe provides this one itself, ignore
+ continue
+ best = bb.providers.filterProvidersRunTime(all_p, rrec,
+ self.bbhandler.config_data,
+ self.bbhandler.cooker_data)[0][0]
+ self.check_cross_depends("RRECOMMENDS", layername, f, best, args.filenames, ignore_layers)
+
+ # The inherit class
+ cls_re = re.compile('classes/')
+ if f in self.bbhandler.cooker_data.inherits:
+ inherits = self.bbhandler.cooker_data.inherits[f]
+ for cls in inherits:
+ # The inherits' format is [classes/cls, /path/to/classes/cls]
+ # ignore the classes/cls.
+ if not cls_re.match(cls):
+ classname = os.path.splitext(os.path.basename(cls))[0]
+ if classname in global_inherit:
+ continue
+ inherit_layername = self.get_file_layer(cls)
+ if inherit_layername != layername and not inherit_layername in ignore_layers:
+ if not args.filenames:
+ f_short = self.remove_layer_prefix(f)
+ cls = self.remove_layer_prefix(cls)
+ else:
+ f_short = f
+ logger.plain("%s inherits %s" % (f_short, cls))
+
+ # The 'require/include xxx' in the bb file
+ pv_re = re.compile(r"\${PV}")
+ with open(f, 'r') as fnfile:
+ line = fnfile.readline()
+ while line:
+ m, keyword = self.match_require_include(line)
+ # Found the 'require/include xxxx'
+ if m:
+ needed_file = m.group(1)
+ # Replace the ${PV} with the real PV
+ if pv_re.search(needed_file) and f in self.bbhandler.cooker_data.pkg_pepvpr:
+ pv = self.bbhandler.cooker_data.pkg_pepvpr[f][1]
+ needed_file = re.sub(r"\${PV}", pv, needed_file)
+ self.print_cross_files(bbpath, keyword, layername, f, needed_file, args.filenames, ignore_layers)
+ line = fnfile.readline()
+
+ # The "require/include xxx" in conf/machine/*.conf, .inc and .bbclass
+ conf_re = re.compile(".*/conf/machine/[^\/]*\.conf$")
+ inc_re = re.compile(".*\.inc$")
+ # The "inherit xxx" in .bbclass
+ bbclass_re = re.compile(".*\.bbclass$")
+ for layerdir in self.bblayers:
+ layername = self.get_layer_name(layerdir)
+ for dirpath, dirnames, filenames in os.walk(layerdir):
+ for name in filenames:
+ f = os.path.join(dirpath, name)
+ s = conf_re.match(f) or inc_re.match(f) or bbclass_re.match(f)
+ if s:
+ with open(f, 'r') as ffile:
+ line = ffile.readline()
+ while line:
+ m, keyword = self.match_require_include(line)
+ # Only bbclass has the "inherit xxx" here.
+ bbclass=""
+ if not m and f.endswith(".bbclass"):
+ m, keyword = self.match_inherit(line)
+ bbclass=".bbclass"
+ # Find a 'require/include xxxx'
+ if m:
+ self.print_cross_files(bbpath, keyword, layername, f, m.group(1) + bbclass, args.filenames, ignore_layers)
+ line = ffile.readline()
+
+ def print_cross_files(self, bbpath, keyword, layername, f, needed_filename, show_filenames, ignore_layers):
+ """Print the depends that crosses a layer boundary"""
+ needed_file = bb.utils.which(bbpath, needed_filename)
+ if needed_file:
+ # Which layer is this file from
+ needed_layername = self.get_file_layer(needed_file)
+ if needed_layername != layername and not needed_layername in ignore_layers:
+ if not show_filenames:
+ f = self.remove_layer_prefix(f)
+ needed_file = self.remove_layer_prefix(needed_file)
+ logger.plain("%s %s %s" %(f, keyword, needed_file))
+
+ def match_inherit(self, line):
+ """Match the inherit xxx line"""
+ return (self.inherit_re.match(line), "inherits")
+
+ def match_require_include(self, line):
+ """Match the require/include xxx line"""
+ m = self.require_re.match(line)
+ keyword = "requires"
+ if not m:
+ m = self.include_re.match(line)
+ keyword = "includes"
+ return (m, keyword)
+
+ def check_cross_depends(self, keyword, layername, f, needed_file, show_filenames, ignore_layers):
+ """Print the DEPENDS/RDEPENDS file that crosses a layer boundary"""
+ best_realfn = bb.cache.Cache.virtualfn2realfn(needed_file)[0]
+ needed_layername = self.get_file_layer(best_realfn)
+ if needed_layername != layername and not needed_layername in ignore_layers:
+ if not show_filenames:
+ f = self.remove_layer_prefix(f)
+ best_realfn = self.remove_layer_prefix(best_realfn)
+
+ logger.plain("%s %s %s" % (f, keyword, best_realfn))
+
+
+def main():
+
+ cmds = Commands()
+
+ def add_command(cmdname, function, *args, **kwargs):
+ # Convert docstring for function to help (one-liner shown in main --help) and description (shown in subcommand --help)
+ docsplit = function.__doc__.splitlines()
+ help = docsplit[0]
+ if len(docsplit) > 1:
+ desc = '\n'.join(docsplit[1:])
+ else:
+ desc = help
+ subparser = subparsers.add_parser(cmdname, *args, help=help, description=desc, formatter_class=argparse.RawTextHelpFormatter, **kwargs)
+ subparser.set_defaults(func=function)
+ return subparser
+
+ parser = argparse.ArgumentParser(description="BitBake layers utility",
+ epilog="Use %(prog)s <subcommand> --help to get help on a specific command")
+ parser.add_argument('-d', '--debug', help='Enable debug output', action='store_true')
+ parser.add_argument('-q', '--quiet', help='Print only errors', action='store_true')
+ subparsers = parser.add_subparsers(title='subcommands', metavar='<subcommand>')
+
+ parser_show_layers = add_command('show-layers', cmds.do_show_layers)
+
+ parser_add_layer = add_command('add-layer', cmds.do_add_layer)
+ parser_add_layer.add_argument('layerdir', help='Layer directory to add')
+
+ parser_remove_layer = add_command('remove-layer', cmds.do_remove_layer)
+ parser_remove_layer.add_argument('layerdir', help='Layer directory to remove (wildcards allowed, enclose in quotes to avoid shell expansion)')
+ parser_remove_layer.set_defaults(func=cmds.do_remove_layer)
+
+ parser_show_overlayed = add_command('show-overlayed', cmds.do_show_overlayed)
+ parser_show_overlayed.add_argument('-f', '--filenames', help='instead of the default formatting, list filenames of higher priority recipes with the ones they overlay indented underneath', action='store_true')
+ parser_show_overlayed.add_argument('-s', '--same-version', help='only list overlayed recipes where the version is the same', action='store_true')
+
+ parser_show_recipes = add_command('show-recipes', cmds.do_show_recipes)
+ parser_show_recipes.add_argument('-f', '--filenames', help='instead of the default formatting, list filenames of higher priority recipes with the ones they overlay indented underneath', action='store_true')
+ parser_show_recipes.add_argument('-m', '--multiple', help='only list where multiple recipes (in the same layer or different layers) exist for the same recipe name', action='store_true')
+ parser_show_recipes.add_argument('-i', '--inherits', help='only list recipes that inherit the named class', metavar='CLASS', default='')
+ parser_show_recipes.add_argument('pnspec', nargs='?', help='optional recipe name specification (wildcards allowed, enclose in quotes to avoid shell expansion)')
+
+ parser_show_appends = add_command('show-appends', cmds.do_show_appends)
+
+ parser_flatten = add_command('flatten', cmds.do_flatten)
+ parser_flatten.add_argument('layer', nargs='*', help='Optional layer(s) to flatten (otherwise all are flattened)')
+ parser_flatten.add_argument('outputdir', help='Output directory')
+
+ parser_show_cross_depends = add_command('show-cross-depends', cmds.do_show_cross_depends)
+ parser_show_cross_depends.add_argument('-f', '--filenames', help='show full file path', action='store_true')
+ parser_show_cross_depends.add_argument('-i', '--ignore', help='ignore dependencies on items in the specified layer(s) (split multiple layer names with commas, no spaces)', metavar='LAYERNAME')
+
+ parser_layerindex_fetch = add_command('layerindex-fetch', cmds.do_layerindex_fetch)
+ parser_layerindex_fetch.add_argument('-n', '--show-only', help='show dependencies and do nothing else', action='store_true')
+ parser_layerindex_fetch.add_argument('-b', '--branch', help='branch name to fetch (default %(default)s)', default='master')
+ parser_layerindex_fetch.add_argument('-i', '--ignore', help='assume the specified layers do not need to be fetched/added (separate multiple layers with commas, no spaces)', metavar='LAYER')
+ parser_layerindex_fetch.add_argument('layername', nargs='+', help='layer to fetch')
+
+ parser_layerindex_show_depends = add_command('layerindex-show-depends', cmds.do_layerindex_show_depends)
+ parser_layerindex_show_depends.add_argument('-b', '--branch', help='branch name to fetch (default %(default)s)', default='master')
+ parser_layerindex_show_depends.add_argument('layername', nargs='+', help='layer to query')
+
+ args = parser.parse_args()
+
+ if args.debug:
+ logger.setLevel(logging.DEBUG)
+ elif args.quiet:
+ logger.setLevel(logging.ERROR)
+
+ try:
+ ret = args.func(args)
+ except UserError as err:
+ logger.error(str(err))
+ ret = 1
+
+ return ret
+
+
+if __name__ == "__main__":
+ try:
+ ret = main()
+ except Exception:
+ ret = 1
+ import traceback
+ traceback.print_exc()
+ sys.exit(ret)
OpenPOWER on IntegriCloud