xref: /OK3568_Linux_fs/yocto/poky/bitbake/lib/bblayers/query.py (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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