summaryrefslogtreecommitdiffstats
path: root/bitbake/bin/bitbake-layers
diff options
context:
space:
mode:
Diffstat (limited to 'bitbake/bin/bitbake-layers')
-rwxr-xr-xbitbake/bin/bitbake-layers726
1 files changed, 726 insertions, 0 deletions
diff --git a/bitbake/bin/bitbake-layers b/bitbake/bin/bitbake-layers
new file mode 100755
index 0000000000..2a7f82998b
--- /dev/null
+++ b/bitbake/bin/bitbake-layers
@@ -0,0 +1,726 @@
1#!/usr/bin/env python
2
3# This script has subcommands which operate against your bitbake layers, either
4# displaying useful information, or acting against them.
5# See the help output for details on available commands.
6
7# Copyright (C) 2011 Mentor Graphics Corporation
8# Copyright (C) 2012 Intel Corporation
9#
10# This program is free software; you can redistribute it and/or modify
11# it under the terms of the GNU General Public License version 2 as
12# published by the Free Software Foundation.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License along
20# with this program; if not, write to the Free Software Foundation, Inc.,
21# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22
23import cmd
24import logging
25import os
26import sys
27import fnmatch
28from collections import defaultdict
29import re
30
31bindir = os.path.dirname(__file__)
32topdir = os.path.dirname(bindir)
33sys.path[0:0] = [os.path.join(topdir, 'lib')]
34
35import bb.cache
36import bb.cooker
37import bb.providers
38import bb.utils
39import bb.tinfoil
40
41
42logger = logging.getLogger('BitBake')
43
44
45def main(args):
46 cmds = Commands()
47 if args:
48 # Allow user to specify e.g. show-layers instead of show_layers
49 args = [args[0].replace('-', '_')] + args[1:]
50 cmds.onecmd(' '.join(args))
51 else:
52 cmds.do_help('')
53 return cmds.returncode
54
55
56class Commands(cmd.Cmd):
57 def __init__(self):
58 self.bbhandler = None
59 self.returncode = 0
60 self.bblayers = []
61 cmd.Cmd.__init__(self)
62
63 def init_bbhandler(self, config_only = False):
64 if not self.bbhandler:
65 self.bbhandler = bb.tinfoil.Tinfoil()
66 self.bblayers = (self.bbhandler.config_data.getVar('BBLAYERS', True) or "").split()
67 self.bbhandler.prepare(config_only)
68
69 def default(self, line):
70 """Handle unrecognised commands"""
71 sys.stderr.write("Unrecognised command or option\n")
72 self.do_help('')
73
74 def do_help(self, topic):
75 """display general help or help on a specified command"""
76 if topic:
77 sys.stdout.write('%s: ' % topic)
78 cmd.Cmd.do_help(self, topic.replace('-', '_'))
79 else:
80 sys.stdout.write("usage: bitbake-layers <command> [arguments]\n\n")
81 sys.stdout.write("Available commands:\n")
82 procnames = list(set(self.get_names()))
83 for procname in procnames:
84 if procname[:3] == 'do_':
85 sys.stdout.write(" %s\n" % procname[3:].replace('_', '-'))
86 doc = getattr(self, procname).__doc__
87 if doc:
88 sys.stdout.write(" %s\n" % doc.splitlines()[0])
89
90 def do_show_layers(self, args):
91 """show current configured layers"""
92 self.init_bbhandler(config_only = True)
93 logger.plain("%s %s %s" % ("layer".ljust(20), "path".ljust(40), "priority"))
94 logger.plain('=' * 74)
95 for layerdir in self.bblayers:
96 layername = self.get_layer_name(layerdir)
97 layerpri = 0
98 for layer, _, regex, pri in self.bbhandler.cooker.recipecache.bbfile_config_priorities:
99 if regex.match(os.path.join(layerdir, 'test')):
100 layerpri = pri
101 break
102
103 logger.plain("%s %s %d" % (layername.ljust(20), layerdir.ljust(40), layerpri))
104
105
106 def version_str(self, pe, pv, pr = None):
107 verstr = "%s" % pv
108 if pr:
109 verstr = "%s-%s" % (verstr, pr)
110 if pe:
111 verstr = "%s:%s" % (pe, verstr)
112 return verstr
113
114
115 def do_show_overlayed(self, args):
116 """list overlayed recipes (where the same recipe exists in another layer)
117
118usage: show-overlayed [-f] [-s]
119
120Lists the names of overlayed recipes and the available versions in each
121layer, with the preferred version first. Note that skipped recipes that
122are overlayed will also be listed, with a " (skipped)" suffix.
123
124Options:
125 -f instead of the default formatting, list filenames of higher priority
126 recipes with the ones they overlay indented underneath
127 -s only list overlayed recipes where the version is the same
128"""
129 self.init_bbhandler()
130
131 show_filenames = False
132 show_same_ver_only = False
133 for arg in args.split():
134 if arg == '-f':
135 show_filenames = True
136 elif arg == '-s':
137 show_same_ver_only = True
138 else:
139 sys.stderr.write("show-overlayed: invalid option %s\n" % arg)
140 self.do_help('')
141 return
142
143 items_listed = self.list_recipes('Overlayed recipes', None, True, show_same_ver_only, show_filenames, True)
144
145 # Check for overlayed .bbclass files
146 classes = defaultdict(list)
147 for layerdir in self.bblayers:
148 classdir = os.path.join(layerdir, 'classes')
149 if os.path.exists(classdir):
150 for classfile in os.listdir(classdir):
151 if os.path.splitext(classfile)[1] == '.bbclass':
152 classes[classfile].append(classdir)
153
154 # Locating classes and other files is a bit more complicated than recipes -
155 # layer priority is not a factor; instead BitBake uses the first matching
156 # file in BBPATH, which is manipulated directly by each layer's
157 # conf/layer.conf in turn, thus the order of layers in bblayers.conf is a
158 # factor - however, each layer.conf is free to either prepend or append to
159 # BBPATH (or indeed do crazy stuff with it). Thus the order in BBPATH might
160 # not be exactly the order present in bblayers.conf either.
161 bbpath = str(self.bbhandler.config_data.getVar('BBPATH', True))
162 overlayed_class_found = False
163 for (classfile, classdirs) in classes.items():
164 if len(classdirs) > 1:
165 if not overlayed_class_found:
166 logger.plain('=== Overlayed classes ===')
167 overlayed_class_found = True
168
169 mainfile = bb.utils.which(bbpath, os.path.join('classes', classfile))
170 if show_filenames:
171 logger.plain('%s' % mainfile)
172 else:
173 # We effectively have to guess the layer here
174 logger.plain('%s:' % classfile)
175 mainlayername = '?'
176 for layerdir in self.bblayers:
177 classdir = os.path.join(layerdir, 'classes')
178 if mainfile.startswith(classdir):
179 mainlayername = self.get_layer_name(layerdir)
180 logger.plain(' %s' % mainlayername)
181 for classdir in classdirs:
182 fullpath = os.path.join(classdir, classfile)
183 if fullpath != mainfile:
184 if show_filenames:
185 print(' %s' % fullpath)
186 else:
187 print(' %s' % self.get_layer_name(os.path.dirname(classdir)))
188
189 if overlayed_class_found:
190 items_listed = True;
191
192 if not items_listed:
193 logger.plain('No overlayed files found.')
194
195
196 def do_show_recipes(self, args):
197 """list available recipes, showing the layer they are provided by
198
199usage: show-recipes [-f] [-m] [pnspec]
200
201Lists the names of overlayed recipes and the available versions in each
202layer, with the preferred version first. Optionally you may specify
203pnspec to match a specified recipe name (supports wildcards). Note that
204skipped recipes will also be listed, with a " (skipped)" suffix.
205
206Options:
207 -f instead of the default formatting, list filenames of higher priority
208 recipes with other available recipes indented underneath
209 -m only list where multiple recipes (in the same layer or different
210 layers) exist for the same recipe name
211"""
212 self.init_bbhandler()
213
214 show_filenames = False
215 show_multi_provider_only = False
216 pnspec = None
217 title = 'Available recipes:'
218 for arg in args.split():
219 if arg == '-f':
220 show_filenames = True
221 elif arg == '-m':
222 show_multi_provider_only = True
223 elif not arg.startswith('-'):
224 pnspec = arg
225 title = 'Available recipes matching %s:' % pnspec
226 else:
227 sys.stderr.write("show-recipes: invalid option %s\n" % arg)
228 self.do_help('')
229 return
230 self.list_recipes(title, pnspec, False, False, show_filenames, show_multi_provider_only)
231
232
233 def list_recipes(self, title, pnspec, show_overlayed_only, show_same_ver_only, show_filenames, show_multi_provider_only):
234 pkg_pn = self.bbhandler.cooker.recipecache.pkg_pn
235 (latest_versions, preferred_versions) = bb.providers.findProviders(self.bbhandler.config_data, self.bbhandler.cooker.recipecache, pkg_pn)
236 allproviders = bb.providers.allProviders(self.bbhandler.cooker.recipecache)
237
238 # Ensure we list skipped recipes
239 # We are largely guessing about PN, PV and the preferred version here,
240 # but we have no choice since skipped recipes are not fully parsed
241 skiplist = self.bbhandler.cooker.skiplist.keys()
242 skiplist.sort( key=lambda fileitem: self.bbhandler.cooker.collection.calc_bbfile_priority(fileitem) )
243 skiplist.reverse()
244 for fn in skiplist:
245 recipe_parts = os.path.splitext(os.path.basename(fn))[0].split('_')
246 p = recipe_parts[0]
247 if len(recipe_parts) > 1:
248 ver = (None, recipe_parts[1], None)
249 else:
250 ver = (None, 'unknown', None)
251 allproviders[p].append((ver, fn))
252 if not p in pkg_pn:
253 pkg_pn[p] = 'dummy'
254 preferred_versions[p] = (ver, fn)
255
256 def print_item(f, pn, ver, layer, ispref):
257 if f in skiplist:
258 skipped = ' (skipped)'
259 else:
260 skipped = ''
261 if show_filenames:
262 if ispref:
263 logger.plain("%s%s", f, skipped)
264 else:
265 logger.plain(" %s%s", f, skipped)
266 else:
267 if ispref:
268 logger.plain("%s:", pn)
269 logger.plain(" %s %s%s", layer.ljust(20), ver, skipped)
270
271 preffiles = []
272 items_listed = False
273 for p in sorted(pkg_pn):
274 if pnspec:
275 if not fnmatch.fnmatch(p, pnspec):
276 continue
277
278 if len(allproviders[p]) > 1 or not show_multi_provider_only:
279 pref = preferred_versions[p]
280 preffile = bb.cache.Cache.virtualfn2realfn(pref[1])[0]
281 if preffile not in preffiles:
282 preflayer = self.get_file_layer(preffile)
283 multilayer = False
284 same_ver = True
285 provs = []
286 for prov in allproviders[p]:
287 provfile = bb.cache.Cache.virtualfn2realfn(prov[1])[0]
288 provlayer = self.get_file_layer(provfile)
289 provs.append((provfile, provlayer, prov[0]))
290 if provlayer != preflayer:
291 multilayer = True
292 if prov[0] != pref[0]:
293 same_ver = False
294
295 if (multilayer or not show_overlayed_only) and (same_ver or not show_same_ver_only):
296 if not items_listed:
297 logger.plain('=== %s ===' % title)
298 items_listed = True
299 print_item(preffile, p, self.version_str(pref[0][0], pref[0][1]), preflayer, True)
300 for (provfile, provlayer, provver) in provs:
301 if provfile != preffile:
302 print_item(provfile, p, self.version_str(provver[0], provver[1]), provlayer, False)
303 # Ensure we don't show two entries for BBCLASSEXTENDed recipes
304 preffiles.append(preffile)
305
306 return items_listed
307
308
309 def do_flatten(self, args):
310 """flattens layer configuration into a separate output directory.
311
312usage: flatten [layer1 layer2 [layer3]...] <outputdir>
313
314Takes the specified layers (or all layers in the current layer
315configuration if none are specified) and builds a "flattened" directory
316containing the contents of all layers, with any overlayed recipes removed
317and bbappends appended to the corresponding recipes. Note that some manual
318cleanup may still be necessary afterwards, in particular:
319
320* where non-recipe files (such as patches) are overwritten (the flatten
321 command will show a warning for these)
322* where anything beyond the normal layer setup has been added to
323 layer.conf (only the lowest priority number layer's layer.conf is used)
324* overridden/appended items from bbappends will need to be tidied up
325* when the flattened layers do not have the same directory structure (the
326 flatten command should show a warning when this will cause a problem)
327
328Warning: if you flatten several layers where another layer is intended to
329be used "inbetween" them (in layer priority order) such that recipes /
330bbappends in the layers interact, and then attempt to use the new output
331layer together with that other layer, you may no longer get the same
332build results (as the layer priority order has effectively changed).
333"""
334 arglist = args.split()
335 if len(arglist) < 1:
336 logger.error('Please specify an output directory')
337 self.do_help('flatten')
338 return
339
340 if len(arglist) == 2:
341 logger.error('If you specify layers to flatten you must specify at least two')
342 self.do_help('flatten')
343 return
344
345 outputdir = arglist[-1]
346 if os.path.exists(outputdir) and os.listdir(outputdir):
347 logger.error('Directory %s exists and is non-empty, please clear it out first' % outputdir)
348 return
349
350 self.init_bbhandler()
351 layers = self.bblayers
352 if len(arglist) > 2:
353 layernames = arglist[:-1]
354 found_layernames = []
355 found_layerdirs = []
356 for layerdir in layers:
357 layername = self.get_layer_name(layerdir)
358 if layername in layernames:
359 found_layerdirs.append(layerdir)
360 found_layernames.append(layername)
361
362 for layername in layernames:
363 if not layername in found_layernames:
364 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])))
365 return
366 layers = found_layerdirs
367 else:
368 layernames = []
369
370 # Ensure a specified path matches our list of layers
371 def layer_path_match(path):
372 for layerdir in layers:
373 if path.startswith(os.path.join(layerdir, '')):
374 return layerdir
375 return None
376
377 appended_recipes = []
378 for layer in layers:
379 overlayed = []
380 for f in self.bbhandler.cooker.collection.overlayed.iterkeys():
381 for of in self.bbhandler.cooker.collection.overlayed[f]:
382 if of.startswith(layer):
383 overlayed.append(of)
384
385 logger.plain('Copying files from %s...' % layer )
386 for root, dirs, files in os.walk(layer):
387 for f1 in files:
388 f1full = os.sep.join([root, f1])
389 if f1full in overlayed:
390 logger.plain(' Skipping overlayed file %s' % f1full )
391 else:
392 ext = os.path.splitext(f1)[1]
393 if ext != '.bbappend':
394 fdest = f1full[len(layer):]
395 fdest = os.path.normpath(os.sep.join([outputdir,fdest]))
396 bb.utils.mkdirhier(os.path.dirname(fdest))
397 if os.path.exists(fdest):
398 if f1 == 'layer.conf' and root.endswith('/conf'):
399 logger.plain(' Skipping layer config file %s' % f1full )
400 continue
401 else:
402 logger.warn('Overwriting file %s', fdest)
403 bb.utils.copyfile(f1full, fdest)
404 if ext == '.bb':
405 if f1 in self.bbhandler.cooker.collection.appendlist:
406 appends = self.bbhandler.cooker.collection.appendlist[f1]
407 if appends:
408 logger.plain(' Applying appends to %s' % fdest )
409 for appendname in appends:
410 if layer_path_match(appendname):
411 self.apply_append(appendname, fdest)
412 appended_recipes.append(f1)
413
414 # Take care of when some layers are excluded and yet we have included bbappends for those recipes
415 for recipename in self.bbhandler.cooker.collection.appendlist.iterkeys():
416 if recipename not in appended_recipes:
417 appends = self.bbhandler.cooker.collection.appendlist[recipename]
418 first_append = None
419 for appendname in appends:
420 layer = layer_path_match(appendname)
421 if layer:
422 if first_append:
423 self.apply_append(appendname, first_append)
424 else:
425 fdest = appendname[len(layer):]
426 fdest = os.path.normpath(os.sep.join([outputdir,fdest]))
427 bb.utils.mkdirhier(os.path.dirname(fdest))
428 bb.utils.copyfile(appendname, fdest)
429 first_append = fdest
430
431 # Get the regex for the first layer in our list (which is where the conf/layer.conf file will
432 # have come from)
433 first_regex = None
434 layerdir = layers[0]
435 for layername, pattern, regex, _ in self.bbhandler.cooker.recipecache.bbfile_config_priorities:
436 if regex.match(os.path.join(layerdir, 'test')):
437 first_regex = regex
438 break
439
440 if first_regex:
441 # Find the BBFILES entries that match (which will have come from this conf/layer.conf file)
442 bbfiles = str(self.bbhandler.config_data.getVar('BBFILES', True)).split()
443 bbfiles_layer = []
444 for item in bbfiles:
445 if first_regex.match(item):
446 newpath = os.path.join(outputdir, item[len(layerdir)+1:])
447 bbfiles_layer.append(newpath)
448
449 if bbfiles_layer:
450 # Check that all important layer files match BBFILES
451 for root, dirs, files in os.walk(outputdir):
452 for f1 in files:
453 ext = os.path.splitext(f1)[1]
454 if ext in ['.bb', '.bbappend']:
455 f1full = os.sep.join([root, f1])
456 entry_found = False
457 for item in bbfiles_layer:
458 if fnmatch.fnmatch(f1full, item):
459 entry_found = True
460 break
461 if not entry_found:
462 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)
463
464 def get_file_layer(self, filename):
465 for layer, _, regex, _ in self.bbhandler.cooker.recipecache.bbfile_config_priorities:
466 if regex.match(filename):
467 for layerdir in self.bblayers:
468 if regex.match(os.path.join(layerdir, 'test')) and re.match(layerdir, filename):
469 return self.get_layer_name(layerdir)
470 return "?"
471
472 def get_file_layerdir(self, filename):
473 for layer, _, regex, _ in self.bbhandler.cooker.recipecache.bbfile_config_priorities:
474 if regex.match(filename):
475 for layerdir in self.bblayers:
476 if regex.match(os.path.join(layerdir, 'test')) and re.match(layerdir, filename):
477 return layerdir
478 return "?"
479
480 def remove_layer_prefix(self, f):
481 """Remove the layer_dir prefix, e.g., f = /path/to/layer_dir/foo/blah, the
482 return value will be: layer_dir/foo/blah"""
483 f_layerdir = self.get_file_layerdir(f)
484 prefix = os.path.join(os.path.dirname(f_layerdir), '')
485 return f[len(prefix):] if f.startswith(prefix) else f
486
487 def get_layer_name(self, layerdir):
488 return os.path.basename(layerdir.rstrip(os.sep))
489
490 def apply_append(self, appendname, recipename):
491 appendfile = open(appendname, 'r')
492 recipefile = open(recipename, 'a')
493 recipefile.write('\n')
494 recipefile.write('##### bbappended from %s #####\n' % self.get_file_layer(appendname))
495 recipefile.writelines(appendfile.readlines())
496 recipefile.close()
497 appendfile.close()
498
499 def do_show_appends(self, args):
500 """list bbappend files and recipe files they apply to
501
502usage: show-appends
503
504Recipes are listed with the bbappends that apply to them as subitems.
505"""
506 self.init_bbhandler()
507 if not self.bbhandler.cooker.collection.appendlist:
508 logger.plain('No append files found')
509 return
510
511 logger.plain('=== Appended recipes ===')
512
513 pnlist = list(self.bbhandler.cooker_data.pkg_pn.keys())
514 pnlist.sort()
515 for pn in pnlist:
516 self.show_appends_for_pn(pn)
517
518 self.show_appends_for_skipped()
519
520 def show_appends_for_pn(self, pn):
521 filenames = self.bbhandler.cooker_data.pkg_pn[pn]
522
523 best = bb.providers.findBestProvider(pn,
524 self.bbhandler.config_data,
525 self.bbhandler.cooker_data,
526 self.bbhandler.cooker_data.pkg_pn)
527 best_filename = os.path.basename(best[3])
528
529 self.show_appends_output(filenames, best_filename)
530
531 def show_appends_for_skipped(self):
532 filenames = [os.path.basename(f)
533 for f in self.bbhandler.cooker.skiplist.iterkeys()]
534 self.show_appends_output(filenames, None, " (skipped)")
535
536 def show_appends_output(self, filenames, best_filename, name_suffix = ''):
537 appended, missing = self.get_appends_for_files(filenames)
538 if appended:
539 for basename, appends in appended:
540 logger.plain('%s%s:', basename, name_suffix)
541 for append in appends:
542 logger.plain(' %s', append)
543
544 if best_filename:
545 if best_filename in missing:
546 logger.warn('%s: missing append for preferred version',
547 best_filename)
548 self.returncode |= 1
549
550
551 def get_appends_for_files(self, filenames):
552 appended, notappended = [], []
553 for filename in filenames:
554 _, cls = bb.cache.Cache.virtualfn2realfn(filename)
555 if cls:
556 continue
557
558 basename = os.path.basename(filename)
559 appends = self.bbhandler.cooker.collection.appendlist.get(basename)
560 if appends:
561 appended.append((basename, list(appends)))
562 else:
563 notappended.append(basename)
564 return appended, notappended
565
566 def do_show_cross_depends(self, args):
567 """figure out the dependency between recipes that crosses a layer boundary.
568
569usage: show-cross-depends [-f]
570
571Figure out the dependency between recipes that crosses a layer boundary.
572
573Options:
574 -f show full file path
575
576NOTE:
577The .bbappend file can impact the dependency.
578"""
579 self.init_bbhandler()
580
581 show_filenames = False
582 for arg in args.split():
583 if arg == '-f':
584 show_filenames = True
585 else:
586 sys.stderr.write("show-cross-depends: invalid option %s\n" % arg)
587 self.do_help('')
588 return
589
590 pkg_fn = self.bbhandler.cooker_data.pkg_fn
591 bbpath = str(self.bbhandler.config_data.getVar('BBPATH', True))
592 self.require_re = re.compile(r"require\s+(.+)")
593 self.include_re = re.compile(r"include\s+(.+)")
594 self.inherit_re = re.compile(r"inherit\s+(.+)")
595
596 # The bb's DEPENDS and RDEPENDS
597 for f in pkg_fn:
598 f = bb.cache.Cache.virtualfn2realfn(f)[0]
599 # Get the layername that the file is in
600 layername = self.get_file_layer(f)
601
602 # The DEPENDS
603 deps = self.bbhandler.cooker_data.deps[f]
604 for pn in deps:
605 if pn in self.bbhandler.cooker_data.pkg_pn:
606 best = bb.providers.findBestProvider(pn,
607 self.bbhandler.config_data,
608 self.bbhandler.cooker_data,
609 self.bbhandler.cooker_data.pkg_pn)
610 self.check_cross_depends("DEPENDS", layername, f, best[3], show_filenames)
611
612 # The RDPENDS
613 all_rdeps = self.bbhandler.cooker_data.rundeps[f].values()
614 # Remove the duplicated or null one.
615 sorted_rdeps = {}
616 # The all_rdeps is the list in list, so we need two for loops
617 for k1 in all_rdeps:
618 for k2 in k1:
619 sorted_rdeps[k2] = 1
620 all_rdeps = sorted_rdeps.keys()
621 for rdep in all_rdeps:
622 all_p = bb.providers.getRuntimeProviders(self.bbhandler.cooker_data, rdep)
623 if all_p:
624 best = bb.providers.filterProvidersRunTime(all_p, rdep,
625 self.bbhandler.config_data,
626 self.bbhandler.cooker_data)[0][0]
627 self.check_cross_depends("RDEPENDS", layername, f, best, show_filenames)
628
629 # The inherit class
630 cls_re = re.compile('classes/')
631 if f in self.bbhandler.cooker_data.inherits:
632 inherits = self.bbhandler.cooker_data.inherits[f]
633 for cls in inherits:
634 # The inherits' format is [classes/cls, /path/to/classes/cls]
635 # ignore the classes/cls.
636 if not cls_re.match(cls):
637 inherit_layername = self.get_file_layer(cls)
638 if inherit_layername != layername:
639 if not show_filenames:
640 f_short = self.remove_layer_prefix(f)
641 cls = self.remove_layer_prefix(cls)
642 else:
643 f_short = f
644 logger.plain("%s inherits %s" % (f_short, cls))
645
646 # The 'require/include xxx' in the bb file
647 pv_re = re.compile(r"\${PV}")
648 fnfile = open(f, 'r')
649 line = fnfile.readline()
650 while line:
651 m, keyword = self.match_require_include(line)
652 # Found the 'require/include xxxx'
653 if m:
654 needed_file = m.group(1)
655 # Replace the ${PV} with the real PV
656 if pv_re.search(needed_file) and f in self.bbhandler.cooker_data.pkg_pepvpr:
657 pv = self.bbhandler.cooker_data.pkg_pepvpr[f][1]
658 needed_file = re.sub(r"\${PV}", pv, needed_file)
659 self.print_cross_files(bbpath, keyword, layername, f, needed_file, show_filenames)
660 line = fnfile.readline()
661 fnfile.close()
662
663 # The "require/include xxx" in conf/machine/*.conf, .inc and .bbclass
664 conf_re = re.compile(".*/conf/machine/[^\/]*\.conf$")
665 inc_re = re.compile(".*\.inc$")
666 # The "inherit xxx" in .bbclass
667 bbclass_re = re.compile(".*\.bbclass$")
668 for layerdir in self.bblayers:
669 layername = self.get_layer_name(layerdir)
670 for dirpath, dirnames, filenames in os.walk(layerdir):
671 for name in filenames:
672 f = os.path.join(dirpath, name)
673 s = conf_re.match(f) or inc_re.match(f) or bbclass_re.match(f)
674 if s:
675 ffile = open(f, 'r')
676 line = ffile.readline()
677 while line:
678 m, keyword = self.match_require_include(line)
679 # Only bbclass has the "inherit xxx" here.
680 bbclass=""
681 if not m and f.endswith(".bbclass"):
682 m, keyword = self.match_inherit(line)
683 bbclass=".bbclass"
684 # Find a 'require/include xxxx'
685 if m:
686 self.print_cross_files(bbpath, keyword, layername, f, m.group(1) + bbclass, show_filenames)
687 line = ffile.readline()
688 ffile.close()
689
690 def print_cross_files(self, bbpath, keyword, layername, f, needed_filename, show_filenames):
691 """Print the depends that crosses a layer boundary"""
692 needed_file = bb.utils.which(bbpath, needed_filename)
693 if needed_file:
694 # Which layer is this file from
695 needed_layername = self.get_file_layer(needed_file)
696 if needed_layername != layername:
697 if not show_filenames:
698 f = self.remove_layer_prefix(f)
699 needed_file = self.remove_layer_prefix(needed_file)
700 logger.plain("%s %s %s" %(f, keyword, needed_file))
701 def match_inherit(self, line):
702 """Match the inherit xxx line"""
703 return (self.inherit_re.match(line), "inherits")
704
705 def match_require_include(self, line):
706 """Match the require/include xxx line"""
707 m = self.require_re.match(line)
708 keyword = "requires"
709 if not m:
710 m = self.include_re.match(line)
711 keyword = "includes"
712 return (m, keyword)
713
714 def check_cross_depends(self, keyword, layername, f, needed_file, show_filenames):
715 """Print the DEPENDS/RDEPENDS file that crosses a layer boundary"""
716 best_realfn = bb.cache.Cache.virtualfn2realfn(needed_file)[0]
717 needed_layername = self.get_file_layer(best_realfn)
718 if needed_layername != layername:
719 if not show_filenames:
720 f = self.remove_layer_prefix(f)
721 best_realfn = self.remove_layer_prefix(best_realfn)
722
723 logger.plain("%s %s %s" % (f, keyword, best_realfn))
724
725if __name__ == '__main__':
726 sys.exit(main(sys.argv[1:]) or 0)