1*4882a593Smuzhiyun# 2*4882a593Smuzhiyun# Copyright BitBake Contributors 3*4882a593Smuzhiyun# 4*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0-only 5*4882a593Smuzhiyun# 6*4882a593Smuzhiyun 7*4882a593Smuzhiyunimport collections 8*4882a593Smuzhiyunimport fnmatch 9*4882a593Smuzhiyunimport logging 10*4882a593Smuzhiyunimport sys 11*4882a593Smuzhiyunimport os 12*4882a593Smuzhiyunimport re 13*4882a593Smuzhiyun 14*4882a593Smuzhiyunimport bb.utils 15*4882a593Smuzhiyun 16*4882a593Smuzhiyunfrom bblayers.common import LayerPlugin 17*4882a593Smuzhiyun 18*4882a593Smuzhiyunlogger = logging.getLogger('bitbake-layers') 19*4882a593Smuzhiyun 20*4882a593Smuzhiyun 21*4882a593Smuzhiyundef plugin_init(plugins): 22*4882a593Smuzhiyun return QueryPlugin() 23*4882a593Smuzhiyun 24*4882a593Smuzhiyun 25*4882a593Smuzhiyunclass QueryPlugin(LayerPlugin): 26*4882a593Smuzhiyun def __init__(self): 27*4882a593Smuzhiyun super(QueryPlugin, self).__init__() 28*4882a593Smuzhiyun self.collection_res = {} 29*4882a593Smuzhiyun 30*4882a593Smuzhiyun def do_show_layers(self, args): 31*4882a593Smuzhiyun """show current configured layers.""" 32*4882a593Smuzhiyun logger.plain("%s %s %s" % ("layer".ljust(20), "path".ljust(40), "priority")) 33*4882a593Smuzhiyun logger.plain('=' * 74) 34*4882a593Smuzhiyun for layer, _, regex, pri in self.tinfoil.cooker.bbfile_config_priorities: 35*4882a593Smuzhiyun layerdir = self.bbfile_collections.get(layer, None) 36*4882a593Smuzhiyun layername = self.get_layer_name(layerdir) 37*4882a593Smuzhiyun logger.plain("%s %s %d" % (layername.ljust(20), layerdir.ljust(40), pri)) 38*4882a593Smuzhiyun 39*4882a593Smuzhiyun def version_str(self, pe, pv, pr = None): 40*4882a593Smuzhiyun verstr = "%s" % pv 41*4882a593Smuzhiyun if pr: 42*4882a593Smuzhiyun verstr = "%s-%s" % (verstr, pr) 43*4882a593Smuzhiyun if pe: 44*4882a593Smuzhiyun verstr = "%s:%s" % (pe, verstr) 45*4882a593Smuzhiyun return verstr 46*4882a593Smuzhiyun 47*4882a593Smuzhiyun def do_show_overlayed(self, args): 48*4882a593Smuzhiyun """list overlayed recipes (where the same recipe exists in another layer) 49*4882a593Smuzhiyun 50*4882a593SmuzhiyunLists the names of overlayed recipes and the available versions in each 51*4882a593Smuzhiyunlayer, with the preferred version first. Note that skipped recipes that 52*4882a593Smuzhiyunare overlayed will also be listed, with a " (skipped)" suffix. 53*4882a593Smuzhiyun""" 54*4882a593Smuzhiyun 55*4882a593Smuzhiyun items_listed = self.list_recipes('Overlayed recipes', None, True, args.same_version, args.filenames, False, True, None, False, None, args.mc) 56*4882a593Smuzhiyun 57*4882a593Smuzhiyun # Check for overlayed .bbclass files 58*4882a593Smuzhiyun classes = collections.defaultdict(list) 59*4882a593Smuzhiyun for layerdir in self.bblayers: 60*4882a593Smuzhiyun classdir = os.path.join(layerdir, 'classes') 61*4882a593Smuzhiyun if os.path.exists(classdir): 62*4882a593Smuzhiyun for classfile in os.listdir(classdir): 63*4882a593Smuzhiyun if os.path.splitext(classfile)[1] == '.bbclass': 64*4882a593Smuzhiyun classes[classfile].append(classdir) 65*4882a593Smuzhiyun 66*4882a593Smuzhiyun # Locating classes and other files is a bit more complicated than recipes - 67*4882a593Smuzhiyun # layer priority is not a factor; instead BitBake uses the first matching 68*4882a593Smuzhiyun # file in BBPATH, which is manipulated directly by each layer's 69*4882a593Smuzhiyun # conf/layer.conf in turn, thus the order of layers in bblayers.conf is a 70*4882a593Smuzhiyun # factor - however, each layer.conf is free to either prepend or append to 71*4882a593Smuzhiyun # BBPATH (or indeed do crazy stuff with it). Thus the order in BBPATH might 72*4882a593Smuzhiyun # not be exactly the order present in bblayers.conf either. 73*4882a593Smuzhiyun bbpath = str(self.tinfoil.config_data.getVar('BBPATH')) 74*4882a593Smuzhiyun overlayed_class_found = False 75*4882a593Smuzhiyun for (classfile, classdirs) in classes.items(): 76*4882a593Smuzhiyun if len(classdirs) > 1: 77*4882a593Smuzhiyun if not overlayed_class_found: 78*4882a593Smuzhiyun logger.plain('=== Overlayed classes ===') 79*4882a593Smuzhiyun overlayed_class_found = True 80*4882a593Smuzhiyun 81*4882a593Smuzhiyun mainfile = bb.utils.which(bbpath, os.path.join('classes', classfile)) 82*4882a593Smuzhiyun if args.filenames: 83*4882a593Smuzhiyun logger.plain('%s' % mainfile) 84*4882a593Smuzhiyun else: 85*4882a593Smuzhiyun # We effectively have to guess the layer here 86*4882a593Smuzhiyun logger.plain('%s:' % classfile) 87*4882a593Smuzhiyun mainlayername = '?' 88*4882a593Smuzhiyun for layerdir in self.bblayers: 89*4882a593Smuzhiyun classdir = os.path.join(layerdir, 'classes') 90*4882a593Smuzhiyun if mainfile.startswith(classdir): 91*4882a593Smuzhiyun mainlayername = self.get_layer_name(layerdir) 92*4882a593Smuzhiyun logger.plain(' %s' % mainlayername) 93*4882a593Smuzhiyun for classdir in classdirs: 94*4882a593Smuzhiyun fullpath = os.path.join(classdir, classfile) 95*4882a593Smuzhiyun if fullpath != mainfile: 96*4882a593Smuzhiyun if args.filenames: 97*4882a593Smuzhiyun print(' %s' % fullpath) 98*4882a593Smuzhiyun else: 99*4882a593Smuzhiyun print(' %s' % self.get_layer_name(os.path.dirname(classdir))) 100*4882a593Smuzhiyun 101*4882a593Smuzhiyun if overlayed_class_found: 102*4882a593Smuzhiyun items_listed = True; 103*4882a593Smuzhiyun 104*4882a593Smuzhiyun if not items_listed: 105*4882a593Smuzhiyun logger.plain('No overlayed files found.') 106*4882a593Smuzhiyun 107*4882a593Smuzhiyun def do_show_recipes(self, args): 108*4882a593Smuzhiyun """list available recipes, showing the layer they are provided by 109*4882a593Smuzhiyun 110*4882a593SmuzhiyunLists the names of recipes and the available versions in each 111*4882a593Smuzhiyunlayer, with the preferred version first. Optionally you may specify 112*4882a593Smuzhiyunpnspec to match a specified recipe name (supports wildcards). Note that 113*4882a593Smuzhiyunskipped recipes will also be listed, with a " (skipped)" suffix. 114*4882a593Smuzhiyun""" 115*4882a593Smuzhiyun 116*4882a593Smuzhiyun inheritlist = args.inherits.split(',') if args.inherits else [] 117*4882a593Smuzhiyun if inheritlist or args.pnspec or args.multiple: 118*4882a593Smuzhiyun title = 'Matching recipes:' 119*4882a593Smuzhiyun else: 120*4882a593Smuzhiyun title = 'Available recipes:' 121*4882a593Smuzhiyun self.list_recipes(title, args.pnspec, False, False, args.filenames, args.recipes_only, args.multiple, args.layer, args.bare, inheritlist, args.mc) 122*4882a593Smuzhiyun 123*4882a593Smuzhiyun def list_recipes(self, title, pnspec, show_overlayed_only, show_same_ver_only, show_filenames, show_recipes_only, show_multi_provider_only, selected_layer, bare, inherits, mc): 124*4882a593Smuzhiyun if inherits: 125*4882a593Smuzhiyun bbpath = str(self.tinfoil.config_data.getVar('BBPATH')) 126*4882a593Smuzhiyun for classname in inherits: 127*4882a593Smuzhiyun classfile = 'classes/%s.bbclass' % classname 128*4882a593Smuzhiyun if not bb.utils.which(bbpath, classfile, history=False): 129*4882a593Smuzhiyun logger.error('No class named %s found in BBPATH', classfile) 130*4882a593Smuzhiyun sys.exit(1) 131*4882a593Smuzhiyun 132*4882a593Smuzhiyun pkg_pn = self.tinfoil.cooker.recipecaches[mc].pkg_pn 133*4882a593Smuzhiyun (latest_versions, preferred_versions, required_versions) = self.tinfoil.find_providers(mc) 134*4882a593Smuzhiyun allproviders = self.tinfoil.get_all_providers(mc) 135*4882a593Smuzhiyun 136*4882a593Smuzhiyun # Ensure we list skipped recipes 137*4882a593Smuzhiyun # We are largely guessing about PN, PV and the preferred version here, 138*4882a593Smuzhiyun # but we have no choice since skipped recipes are not fully parsed 139*4882a593Smuzhiyun skiplist = list(self.tinfoil.cooker.skiplist.keys()) 140*4882a593Smuzhiyun mcspec = 'mc:%s:' % mc 141*4882a593Smuzhiyun if mc: 142*4882a593Smuzhiyun skiplist = [s[len(mcspec):] for s in skiplist if s.startswith(mcspec)] 143*4882a593Smuzhiyun 144*4882a593Smuzhiyun for fn in skiplist: 145*4882a593Smuzhiyun recipe_parts = os.path.splitext(os.path.basename(fn))[0].split('_') 146*4882a593Smuzhiyun p = recipe_parts[0] 147*4882a593Smuzhiyun if len(recipe_parts) > 1: 148*4882a593Smuzhiyun ver = (None, recipe_parts[1], None) 149*4882a593Smuzhiyun else: 150*4882a593Smuzhiyun ver = (None, 'unknown', None) 151*4882a593Smuzhiyun allproviders[p].append((ver, fn)) 152*4882a593Smuzhiyun if not p in pkg_pn: 153*4882a593Smuzhiyun pkg_pn[p] = 'dummy' 154*4882a593Smuzhiyun preferred_versions[p] = (ver, fn) 155*4882a593Smuzhiyun 156*4882a593Smuzhiyun def print_item(f, pn, ver, layer, ispref): 157*4882a593Smuzhiyun if not selected_layer or layer == selected_layer: 158*4882a593Smuzhiyun if not bare and f in skiplist: 159*4882a593Smuzhiyun skipped = ' (skipped: %s)' % self.tinfoil.cooker.skiplist[f].skipreason 160*4882a593Smuzhiyun else: 161*4882a593Smuzhiyun skipped = '' 162*4882a593Smuzhiyun if show_filenames: 163*4882a593Smuzhiyun if ispref: 164*4882a593Smuzhiyun logger.plain("%s%s", f, skipped) 165*4882a593Smuzhiyun else: 166*4882a593Smuzhiyun logger.plain(" %s%s", f, skipped) 167*4882a593Smuzhiyun elif show_recipes_only: 168*4882a593Smuzhiyun if pn not in show_unique_pn: 169*4882a593Smuzhiyun show_unique_pn.append(pn) 170*4882a593Smuzhiyun logger.plain("%s%s", pn, skipped) 171*4882a593Smuzhiyun else: 172*4882a593Smuzhiyun if ispref: 173*4882a593Smuzhiyun logger.plain("%s:", pn) 174*4882a593Smuzhiyun logger.plain(" %s %s%s", layer.ljust(20), ver, skipped) 175*4882a593Smuzhiyun 176*4882a593Smuzhiyun global_inherit = (self.tinfoil.config_data.getVar('INHERIT') or "").split() 177*4882a593Smuzhiyun cls_re = re.compile('classes/') 178*4882a593Smuzhiyun 179*4882a593Smuzhiyun preffiles = [] 180*4882a593Smuzhiyun show_unique_pn = [] 181*4882a593Smuzhiyun items_listed = False 182*4882a593Smuzhiyun for p in sorted(pkg_pn): 183*4882a593Smuzhiyun if pnspec: 184*4882a593Smuzhiyun found=False 185*4882a593Smuzhiyun for pnm in pnspec: 186*4882a593Smuzhiyun if fnmatch.fnmatch(p, pnm): 187*4882a593Smuzhiyun found=True 188*4882a593Smuzhiyun break 189*4882a593Smuzhiyun if not found: 190*4882a593Smuzhiyun continue 191*4882a593Smuzhiyun 192*4882a593Smuzhiyun if len(allproviders[p]) > 1 or not show_multi_provider_only: 193*4882a593Smuzhiyun pref = preferred_versions[p] 194*4882a593Smuzhiyun realfn = bb.cache.virtualfn2realfn(pref[1]) 195*4882a593Smuzhiyun preffile = realfn[0] 196*4882a593Smuzhiyun 197*4882a593Smuzhiyun # We only display once per recipe, we should prefer non extended versions of the 198*4882a593Smuzhiyun # recipe if present (so e.g. in OpenEmbedded, openssl rather than nativesdk-openssl 199*4882a593Smuzhiyun # which would otherwise sort first). 200*4882a593Smuzhiyun if realfn[1] and realfn[0] in self.tinfoil.cooker.recipecaches[mc].pkg_fn: 201*4882a593Smuzhiyun continue 202*4882a593Smuzhiyun 203*4882a593Smuzhiyun if inherits: 204*4882a593Smuzhiyun matchcount = 0 205*4882a593Smuzhiyun recipe_inherits = self.tinfoil.cooker_data.inherits.get(preffile, []) 206*4882a593Smuzhiyun for cls in recipe_inherits: 207*4882a593Smuzhiyun if cls_re.match(cls): 208*4882a593Smuzhiyun continue 209*4882a593Smuzhiyun classname = os.path.splitext(os.path.basename(cls))[0] 210*4882a593Smuzhiyun if classname in global_inherit: 211*4882a593Smuzhiyun continue 212*4882a593Smuzhiyun elif classname in inherits: 213*4882a593Smuzhiyun matchcount += 1 214*4882a593Smuzhiyun if matchcount != len(inherits): 215*4882a593Smuzhiyun # No match - skip this recipe 216*4882a593Smuzhiyun continue 217*4882a593Smuzhiyun 218*4882a593Smuzhiyun if preffile not in preffiles: 219*4882a593Smuzhiyun preflayer = self.get_file_layer(preffile) 220*4882a593Smuzhiyun multilayer = False 221*4882a593Smuzhiyun same_ver = True 222*4882a593Smuzhiyun provs = [] 223*4882a593Smuzhiyun for prov in allproviders[p]: 224*4882a593Smuzhiyun provfile = bb.cache.virtualfn2realfn(prov[1])[0] 225*4882a593Smuzhiyun provlayer = self.get_file_layer(provfile) 226*4882a593Smuzhiyun provs.append((provfile, provlayer, prov[0])) 227*4882a593Smuzhiyun if provlayer != preflayer: 228*4882a593Smuzhiyun multilayer = True 229*4882a593Smuzhiyun if prov[0] != pref[0]: 230*4882a593Smuzhiyun same_ver = False 231*4882a593Smuzhiyun if (multilayer or not show_overlayed_only) and (same_ver or not show_same_ver_only): 232*4882a593Smuzhiyun if not items_listed: 233*4882a593Smuzhiyun logger.plain('=== %s ===' % title) 234*4882a593Smuzhiyun items_listed = True 235*4882a593Smuzhiyun print_item(preffile, p, self.version_str(pref[0][0], pref[0][1]), preflayer, True) 236*4882a593Smuzhiyun for (provfile, provlayer, provver) in provs: 237*4882a593Smuzhiyun if provfile != preffile: 238*4882a593Smuzhiyun print_item(provfile, p, self.version_str(provver[0], provver[1]), provlayer, False) 239*4882a593Smuzhiyun # Ensure we don't show two entries for BBCLASSEXTENDed recipes 240*4882a593Smuzhiyun preffiles.append(preffile) 241*4882a593Smuzhiyun 242*4882a593Smuzhiyun return items_listed 243*4882a593Smuzhiyun 244*4882a593Smuzhiyun def get_file_layer(self, filename): 245*4882a593Smuzhiyun layerdir = self.get_file_layerdir(filename) 246*4882a593Smuzhiyun if layerdir: 247*4882a593Smuzhiyun return self.get_layer_name(layerdir) 248*4882a593Smuzhiyun else: 249*4882a593Smuzhiyun return '?' 250*4882a593Smuzhiyun 251*4882a593Smuzhiyun def get_collection_res(self): 252*4882a593Smuzhiyun if not self.collection_res: 253*4882a593Smuzhiyun self.collection_res = bb.utils.get_collection_res(self.tinfoil.config_data) 254*4882a593Smuzhiyun return self.collection_res 255*4882a593Smuzhiyun 256*4882a593Smuzhiyun def get_file_layerdir(self, filename): 257*4882a593Smuzhiyun layer = bb.utils.get_file_layer(filename, self.tinfoil.config_data, self.get_collection_res()) 258*4882a593Smuzhiyun return self.bbfile_collections.get(layer, None) 259*4882a593Smuzhiyun 260*4882a593Smuzhiyun def remove_layer_prefix(self, f): 261*4882a593Smuzhiyun """Remove the layer_dir prefix, e.g., f = /path/to/layer_dir/foo/blah, the 262*4882a593Smuzhiyun return value will be: layer_dir/foo/blah""" 263*4882a593Smuzhiyun f_layerdir = self.get_file_layerdir(f) 264*4882a593Smuzhiyun if not f_layerdir: 265*4882a593Smuzhiyun return f 266*4882a593Smuzhiyun prefix = os.path.join(os.path.dirname(f_layerdir), '') 267*4882a593Smuzhiyun return f[len(prefix):] if f.startswith(prefix) else f 268*4882a593Smuzhiyun 269*4882a593Smuzhiyun def do_show_appends(self, args): 270*4882a593Smuzhiyun """list bbappend files and recipe files they apply to 271*4882a593Smuzhiyun 272*4882a593SmuzhiyunLists recipes with the bbappends that apply to them as subitems. 273*4882a593Smuzhiyun""" 274*4882a593Smuzhiyun if args.pnspec: 275*4882a593Smuzhiyun logger.plain('=== Matched appended recipes ===') 276*4882a593Smuzhiyun else: 277*4882a593Smuzhiyun logger.plain('=== Appended recipes ===') 278*4882a593Smuzhiyun 279*4882a593Smuzhiyun pnlist = list(self.tinfoil.cooker_data.pkg_pn.keys()) 280*4882a593Smuzhiyun pnlist.sort() 281*4882a593Smuzhiyun appends = False 282*4882a593Smuzhiyun for pn in pnlist: 283*4882a593Smuzhiyun if args.pnspec: 284*4882a593Smuzhiyun found=False 285*4882a593Smuzhiyun for pnm in args.pnspec: 286*4882a593Smuzhiyun if fnmatch.fnmatch(pn, pnm): 287*4882a593Smuzhiyun found=True 288*4882a593Smuzhiyun break 289*4882a593Smuzhiyun if not found: 290*4882a593Smuzhiyun continue 291*4882a593Smuzhiyun 292*4882a593Smuzhiyun if self.show_appends_for_pn(pn): 293*4882a593Smuzhiyun appends = True 294*4882a593Smuzhiyun 295*4882a593Smuzhiyun if not args.pnspec and self.show_appends_for_skipped(): 296*4882a593Smuzhiyun appends = True 297*4882a593Smuzhiyun 298*4882a593Smuzhiyun if not appends: 299*4882a593Smuzhiyun logger.plain('No append files found') 300*4882a593Smuzhiyun 301*4882a593Smuzhiyun def show_appends_for_pn(self, pn): 302*4882a593Smuzhiyun filenames = self.tinfoil.cooker_data.pkg_pn[pn] 303*4882a593Smuzhiyun 304*4882a593Smuzhiyun best = self.tinfoil.find_best_provider(pn) 305*4882a593Smuzhiyun best_filename = os.path.basename(best[3]) 306*4882a593Smuzhiyun 307*4882a593Smuzhiyun return self.show_appends_output(filenames, best_filename) 308*4882a593Smuzhiyun 309*4882a593Smuzhiyun def show_appends_for_skipped(self): 310*4882a593Smuzhiyun filenames = [os.path.basename(f) 311*4882a593Smuzhiyun for f in self.tinfoil.cooker.skiplist.keys()] 312*4882a593Smuzhiyun return self.show_appends_output(filenames, None, " (skipped)") 313*4882a593Smuzhiyun 314*4882a593Smuzhiyun def show_appends_output(self, filenames, best_filename, name_suffix = ''): 315*4882a593Smuzhiyun appended, missing = self.get_appends_for_files(filenames) 316*4882a593Smuzhiyun if appended: 317*4882a593Smuzhiyun for basename, appends in appended: 318*4882a593Smuzhiyun logger.plain('%s%s:', basename, name_suffix) 319*4882a593Smuzhiyun for append in appends: 320*4882a593Smuzhiyun logger.plain(' %s', append) 321*4882a593Smuzhiyun 322*4882a593Smuzhiyun if best_filename: 323*4882a593Smuzhiyun if best_filename in missing: 324*4882a593Smuzhiyun logger.warning('%s: missing append for preferred version', 325*4882a593Smuzhiyun best_filename) 326*4882a593Smuzhiyun return True 327*4882a593Smuzhiyun else: 328*4882a593Smuzhiyun return False 329*4882a593Smuzhiyun 330*4882a593Smuzhiyun def get_appends_for_files(self, filenames): 331*4882a593Smuzhiyun appended, notappended = [], [] 332*4882a593Smuzhiyun for filename in filenames: 333*4882a593Smuzhiyun _, cls, mc = bb.cache.virtualfn2realfn(filename) 334*4882a593Smuzhiyun if cls: 335*4882a593Smuzhiyun continue 336*4882a593Smuzhiyun 337*4882a593Smuzhiyun basename = os.path.basename(filename) 338*4882a593Smuzhiyun appends = self.tinfoil.cooker.collections[mc].get_file_appends(basename) 339*4882a593Smuzhiyun if appends: 340*4882a593Smuzhiyun appended.append((basename, list(appends))) 341*4882a593Smuzhiyun else: 342*4882a593Smuzhiyun notappended.append(basename) 343*4882a593Smuzhiyun return appended, notappended 344*4882a593Smuzhiyun 345*4882a593Smuzhiyun def do_show_cross_depends(self, args): 346*4882a593Smuzhiyun """Show dependencies between recipes that cross layer boundaries. 347*4882a593Smuzhiyun 348*4882a593SmuzhiyunFigure out the dependencies between recipes that cross layer boundaries. 349*4882a593Smuzhiyun 350*4882a593SmuzhiyunNOTE: .bbappend files can impact the dependencies. 351*4882a593Smuzhiyun""" 352*4882a593Smuzhiyun ignore_layers = (args.ignore or '').split(',') 353*4882a593Smuzhiyun 354*4882a593Smuzhiyun pkg_fn = self.tinfoil.cooker_data.pkg_fn 355*4882a593Smuzhiyun bbpath = str(self.tinfoil.config_data.getVar('BBPATH')) 356*4882a593Smuzhiyun self.require_re = re.compile(r"require\s+(.+)") 357*4882a593Smuzhiyun self.include_re = re.compile(r"include\s+(.+)") 358*4882a593Smuzhiyun self.inherit_re = re.compile(r"inherit\s+(.+)") 359*4882a593Smuzhiyun 360*4882a593Smuzhiyun global_inherit = (self.tinfoil.config_data.getVar('INHERIT') or "").split() 361*4882a593Smuzhiyun 362*4882a593Smuzhiyun # The bb's DEPENDS and RDEPENDS 363*4882a593Smuzhiyun for f in pkg_fn: 364*4882a593Smuzhiyun f = bb.cache.virtualfn2realfn(f)[0] 365*4882a593Smuzhiyun # Get the layername that the file is in 366*4882a593Smuzhiyun layername = self.get_file_layer(f) 367*4882a593Smuzhiyun 368*4882a593Smuzhiyun # The DEPENDS 369*4882a593Smuzhiyun deps = self.tinfoil.cooker_data.deps[f] 370*4882a593Smuzhiyun for pn in deps: 371*4882a593Smuzhiyun if pn in self.tinfoil.cooker_data.pkg_pn: 372*4882a593Smuzhiyun best = self.tinfoil.find_best_provider(pn) 373*4882a593Smuzhiyun self.check_cross_depends("DEPENDS", layername, f, best[3], args.filenames, ignore_layers) 374*4882a593Smuzhiyun 375*4882a593Smuzhiyun # The RDPENDS 376*4882a593Smuzhiyun all_rdeps = self.tinfoil.cooker_data.rundeps[f].values() 377*4882a593Smuzhiyun # Remove the duplicated or null one. 378*4882a593Smuzhiyun sorted_rdeps = {} 379*4882a593Smuzhiyun # The all_rdeps is the list in list, so we need two for loops 380*4882a593Smuzhiyun for k1 in all_rdeps: 381*4882a593Smuzhiyun for k2 in k1: 382*4882a593Smuzhiyun sorted_rdeps[k2] = 1 383*4882a593Smuzhiyun all_rdeps = sorted_rdeps.keys() 384*4882a593Smuzhiyun for rdep in all_rdeps: 385*4882a593Smuzhiyun all_p, best = self.tinfoil.get_runtime_providers(rdep) 386*4882a593Smuzhiyun if all_p: 387*4882a593Smuzhiyun if f in all_p: 388*4882a593Smuzhiyun # The recipe provides this one itself, ignore 389*4882a593Smuzhiyun continue 390*4882a593Smuzhiyun self.check_cross_depends("RDEPENDS", layername, f, best, args.filenames, ignore_layers) 391*4882a593Smuzhiyun 392*4882a593Smuzhiyun # The RRECOMMENDS 393*4882a593Smuzhiyun all_rrecs = self.tinfoil.cooker_data.runrecs[f].values() 394*4882a593Smuzhiyun # Remove the duplicated or null one. 395*4882a593Smuzhiyun sorted_rrecs = {} 396*4882a593Smuzhiyun # The all_rrecs is the list in list, so we need two for loops 397*4882a593Smuzhiyun for k1 in all_rrecs: 398*4882a593Smuzhiyun for k2 in k1: 399*4882a593Smuzhiyun sorted_rrecs[k2] = 1 400*4882a593Smuzhiyun all_rrecs = sorted_rrecs.keys() 401*4882a593Smuzhiyun for rrec in all_rrecs: 402*4882a593Smuzhiyun all_p, best = self.tinfoil.get_runtime_providers(rrec) 403*4882a593Smuzhiyun if all_p: 404*4882a593Smuzhiyun if f in all_p: 405*4882a593Smuzhiyun # The recipe provides this one itself, ignore 406*4882a593Smuzhiyun continue 407*4882a593Smuzhiyun self.check_cross_depends("RRECOMMENDS", layername, f, best, args.filenames, ignore_layers) 408*4882a593Smuzhiyun 409*4882a593Smuzhiyun # The inherit class 410*4882a593Smuzhiyun cls_re = re.compile('classes/') 411*4882a593Smuzhiyun if f in self.tinfoil.cooker_data.inherits: 412*4882a593Smuzhiyun inherits = self.tinfoil.cooker_data.inherits[f] 413*4882a593Smuzhiyun for cls in inherits: 414*4882a593Smuzhiyun # The inherits' format is [classes/cls, /path/to/classes/cls] 415*4882a593Smuzhiyun # ignore the classes/cls. 416*4882a593Smuzhiyun if not cls_re.match(cls): 417*4882a593Smuzhiyun classname = os.path.splitext(os.path.basename(cls))[0] 418*4882a593Smuzhiyun if classname in global_inherit: 419*4882a593Smuzhiyun continue 420*4882a593Smuzhiyun inherit_layername = self.get_file_layer(cls) 421*4882a593Smuzhiyun if inherit_layername != layername and not inherit_layername in ignore_layers: 422*4882a593Smuzhiyun if not args.filenames: 423*4882a593Smuzhiyun f_short = self.remove_layer_prefix(f) 424*4882a593Smuzhiyun cls = self.remove_layer_prefix(cls) 425*4882a593Smuzhiyun else: 426*4882a593Smuzhiyun f_short = f 427*4882a593Smuzhiyun logger.plain("%s inherits %s" % (f_short, cls)) 428*4882a593Smuzhiyun 429*4882a593Smuzhiyun # The 'require/include xxx' in the bb file 430*4882a593Smuzhiyun pv_re = re.compile(r"\${PV}") 431*4882a593Smuzhiyun with open(f, 'r') as fnfile: 432*4882a593Smuzhiyun line = fnfile.readline() 433*4882a593Smuzhiyun while line: 434*4882a593Smuzhiyun m, keyword = self.match_require_include(line) 435*4882a593Smuzhiyun # Found the 'require/include xxxx' 436*4882a593Smuzhiyun if m: 437*4882a593Smuzhiyun needed_file = m.group(1) 438*4882a593Smuzhiyun # Replace the ${PV} with the real PV 439*4882a593Smuzhiyun if pv_re.search(needed_file) and f in self.tinfoil.cooker_data.pkg_pepvpr: 440*4882a593Smuzhiyun pv = self.tinfoil.cooker_data.pkg_pepvpr[f][1] 441*4882a593Smuzhiyun needed_file = re.sub(r"\${PV}", pv, needed_file) 442*4882a593Smuzhiyun self.print_cross_files(bbpath, keyword, layername, f, needed_file, args.filenames, ignore_layers) 443*4882a593Smuzhiyun line = fnfile.readline() 444*4882a593Smuzhiyun 445*4882a593Smuzhiyun # The "require/include xxx" in conf/machine/*.conf, .inc and .bbclass 446*4882a593Smuzhiyun conf_re = re.compile(r".*/conf/machine/[^\/]*\.conf$") 447*4882a593Smuzhiyun inc_re = re.compile(r".*\.inc$") 448*4882a593Smuzhiyun # The "inherit xxx" in .bbclass 449*4882a593Smuzhiyun bbclass_re = re.compile(r".*\.bbclass$") 450*4882a593Smuzhiyun for layerdir in self.bblayers: 451*4882a593Smuzhiyun layername = self.get_layer_name(layerdir) 452*4882a593Smuzhiyun for dirpath, dirnames, filenames in os.walk(layerdir): 453*4882a593Smuzhiyun for name in filenames: 454*4882a593Smuzhiyun f = os.path.join(dirpath, name) 455*4882a593Smuzhiyun s = conf_re.match(f) or inc_re.match(f) or bbclass_re.match(f) 456*4882a593Smuzhiyun if s: 457*4882a593Smuzhiyun with open(f, 'r') as ffile: 458*4882a593Smuzhiyun line = ffile.readline() 459*4882a593Smuzhiyun while line: 460*4882a593Smuzhiyun m, keyword = self.match_require_include(line) 461*4882a593Smuzhiyun # Only bbclass has the "inherit xxx" here. 462*4882a593Smuzhiyun bbclass="" 463*4882a593Smuzhiyun if not m and f.endswith(".bbclass"): 464*4882a593Smuzhiyun m, keyword = self.match_inherit(line) 465*4882a593Smuzhiyun bbclass=".bbclass" 466*4882a593Smuzhiyun # Find a 'require/include xxxx' 467*4882a593Smuzhiyun if m: 468*4882a593Smuzhiyun self.print_cross_files(bbpath, keyword, layername, f, m.group(1) + bbclass, args.filenames, ignore_layers) 469*4882a593Smuzhiyun line = ffile.readline() 470*4882a593Smuzhiyun 471*4882a593Smuzhiyun def print_cross_files(self, bbpath, keyword, layername, f, needed_filename, show_filenames, ignore_layers): 472*4882a593Smuzhiyun """Print the depends that crosses a layer boundary""" 473*4882a593Smuzhiyun needed_file = bb.utils.which(bbpath, needed_filename) 474*4882a593Smuzhiyun if needed_file: 475*4882a593Smuzhiyun # Which layer is this file from 476*4882a593Smuzhiyun needed_layername = self.get_file_layer(needed_file) 477*4882a593Smuzhiyun if needed_layername != layername and not needed_layername in ignore_layers: 478*4882a593Smuzhiyun if not show_filenames: 479*4882a593Smuzhiyun f = self.remove_layer_prefix(f) 480*4882a593Smuzhiyun needed_file = self.remove_layer_prefix(needed_file) 481*4882a593Smuzhiyun logger.plain("%s %s %s" %(f, keyword, needed_file)) 482*4882a593Smuzhiyun 483*4882a593Smuzhiyun def match_inherit(self, line): 484*4882a593Smuzhiyun """Match the inherit xxx line""" 485*4882a593Smuzhiyun return (self.inherit_re.match(line), "inherits") 486*4882a593Smuzhiyun 487*4882a593Smuzhiyun def match_require_include(self, line): 488*4882a593Smuzhiyun """Match the require/include xxx line""" 489*4882a593Smuzhiyun m = self.require_re.match(line) 490*4882a593Smuzhiyun keyword = "requires" 491*4882a593Smuzhiyun if not m: 492*4882a593Smuzhiyun m = self.include_re.match(line) 493*4882a593Smuzhiyun keyword = "includes" 494*4882a593Smuzhiyun return (m, keyword) 495*4882a593Smuzhiyun 496*4882a593Smuzhiyun def check_cross_depends(self, keyword, layername, f, needed_file, show_filenames, ignore_layers): 497*4882a593Smuzhiyun """Print the DEPENDS/RDEPENDS file that crosses a layer boundary""" 498*4882a593Smuzhiyun best_realfn = bb.cache.virtualfn2realfn(needed_file)[0] 499*4882a593Smuzhiyun needed_layername = self.get_file_layer(best_realfn) 500*4882a593Smuzhiyun if needed_layername != layername and not needed_layername in ignore_layers: 501*4882a593Smuzhiyun if not show_filenames: 502*4882a593Smuzhiyun f = self.remove_layer_prefix(f) 503*4882a593Smuzhiyun best_realfn = self.remove_layer_prefix(best_realfn) 504*4882a593Smuzhiyun 505*4882a593Smuzhiyun logger.plain("%s %s %s" % (f, keyword, best_realfn)) 506*4882a593Smuzhiyun 507*4882a593Smuzhiyun def register_commands(self, sp): 508*4882a593Smuzhiyun self.add_command(sp, 'show-layers', self.do_show_layers, parserecipes=False) 509*4882a593Smuzhiyun 510*4882a593Smuzhiyun parser_show_overlayed = self.add_command(sp, 'show-overlayed', self.do_show_overlayed) 511*4882a593Smuzhiyun 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') 512*4882a593Smuzhiyun parser_show_overlayed.add_argument('-s', '--same-version', help='only list overlayed recipes where the version is the same', action='store_true') 513*4882a593Smuzhiyun parser_show_overlayed.add_argument('--mc', help='use specified multiconfig', default='') 514*4882a593Smuzhiyun 515*4882a593Smuzhiyun parser_show_recipes = self.add_command(sp, 'show-recipes', self.do_show_recipes) 516*4882a593Smuzhiyun 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') 517*4882a593Smuzhiyun parser_show_recipes.add_argument('-r', '--recipes-only', help='instead of the default formatting, list recipes only', action='store_true') 518*4882a593Smuzhiyun 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') 519*4882a593Smuzhiyun parser_show_recipes.add_argument('-i', '--inherits', help='only list recipes that inherit the named class(es) - separate multiple classes using , (without spaces)', metavar='CLASS', default='') 520*4882a593Smuzhiyun parser_show_recipes.add_argument('-l', '--layer', help='only list recipes from the selected layer', default='') 521*4882a593Smuzhiyun parser_show_recipes.add_argument('-b', '--bare', help='output just names without the "(skipped)" marker', action='store_true') 522*4882a593Smuzhiyun parser_show_recipes.add_argument('--mc', help='use specified multiconfig', default='') 523*4882a593Smuzhiyun parser_show_recipes.add_argument('pnspec', nargs='*', help='optional recipe name specification (wildcards allowed, enclose in quotes to avoid shell expansion)') 524*4882a593Smuzhiyun 525*4882a593Smuzhiyun parser_show_appends = self.add_command(sp, 'show-appends', self.do_show_appends) 526*4882a593Smuzhiyun parser_show_appends.add_argument('pnspec', nargs='*', help='optional recipe name specification (wildcards allowed, enclose in quotes to avoid shell expansion)') 527*4882a593Smuzhiyun 528*4882a593Smuzhiyun parser_show_cross_depends = self.add_command(sp, 'show-cross-depends', self.do_show_cross_depends) 529*4882a593Smuzhiyun parser_show_cross_depends.add_argument('-f', '--filenames', help='show full file path', action='store_true') 530*4882a593Smuzhiyun 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') 531