diff options
author | Tudor Florea <tudor.florea@enea.com> | 2015-10-09 22:59:03 +0200 |
---|---|---|
committer | Tudor Florea <tudor.florea@enea.com> | 2015-10-09 22:59:03 +0200 |
commit | 972dcfcdbfe75dcfeb777150c136576cf1a71e99 (patch) | |
tree | 97a61cd7e293d7ae9d56ef7ed0f81253365bb026 /bitbake/bin/bitbake-layers | |
download | poky-972dcfcdbfe75dcfeb777150c136576cf1a71e99.tar.gz |
initial commit for Enea Linux 5.0 arm
Signed-off-by: Tudor Florea <tudor.florea@enea.com>
Diffstat (limited to 'bitbake/bin/bitbake-layers')
-rwxr-xr-x | bitbake/bin/bitbake-layers | 758 |
1 files changed, 758 insertions, 0 deletions
diff --git a/bitbake/bin/bitbake-layers b/bitbake/bin/bitbake-layers new file mode 100755 index 0000000000..9964040bf7 --- /dev/null +++ b/bitbake/bin/bitbake-layers | |||
@@ -0,0 +1,758 @@ | |||
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 | |||
23 | import cmd | ||
24 | import logging | ||
25 | import os | ||
26 | import sys | ||
27 | import fnmatch | ||
28 | from collections import defaultdict | ||
29 | import re | ||
30 | |||
31 | bindir = os.path.dirname(__file__) | ||
32 | topdir = os.path.dirname(bindir) | ||
33 | sys.path[0:0] = [os.path.join(topdir, 'lib')] | ||
34 | |||
35 | import bb.cache | ||
36 | import bb.cooker | ||
37 | import bb.providers | ||
38 | import bb.utils | ||
39 | import bb.tinfoil | ||
40 | |||
41 | |||
42 | logger = logging.getLogger('BitBake') | ||
43 | |||
44 | |||
45 | def 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 | |||
56 | class 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 | |||
118 | usage: show-overlayed [-f] [-s] | ||
119 | |||
120 | Lists the names of overlayed recipes and the available versions in each | ||
121 | layer, with the preferred version first. Note that skipped recipes that | ||
122 | are overlayed will also be listed, with a " (skipped)" suffix. | ||
123 | |||
124 | Options: | ||
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 | |||
199 | usage: show-recipes [-f] [-m] [pnspec] | ||
200 | |||
201 | Lists the names of overlayed recipes and the available versions in each | ||
202 | layer, with the preferred version first. Optionally you may specify | ||
203 | pnspec to match a specified recipe name (supports wildcards). Note that | ||
204 | skipped recipes will also be listed, with a " (skipped)" suffix. | ||
205 | |||
206 | Options: | ||
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 | |||
312 | usage: flatten [layer1 layer2 [layer3]...] <outputdir> | ||
313 | |||
314 | Takes the specified layers (or all layers in the current layer | ||
315 | configuration if none are specified) and builds a "flattened" directory | ||
316 | containing the contents of all layers, with any overlayed recipes removed | ||
317 | and bbappends appended to the corresponding recipes. Note that some manual | ||
318 | cleanup 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 | |||
328 | Warning: if you flatten several layers where another layer is intended to | ||
329 | be used "inbetween" them (in layer priority order) such that recipes / | ||
330 | bbappends in the layers interact, and then attempt to use the new output | ||
331 | layer together with that other layer, you may no longer get the same | ||
332 | build 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 | |||
502 | usage: show-appends | ||
503 | |||
504 | Recipes 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.get_file_appends(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 | |||
569 | usage: show-cross-depends [-f] [-i layer1[,layer2[,layer3...]]] | ||
570 | |||
571 | Figure out the dependency between recipes that crosses a layer boundary. | ||
572 | |||
573 | Options: | ||
574 | -f show full file path | ||
575 | -i ignore dependencies on items in the specified layer(s) | ||
576 | |||
577 | NOTE: | ||
578 | The .bbappend file can impact the dependency. | ||
579 | """ | ||
580 | import optparse | ||
581 | |||
582 | parser = optparse.OptionParser(usage="show-cross-depends [-f] [-i layer1[,layer2[,layer3...]]]") | ||
583 | parser.add_option("-f", "", | ||
584 | action="store_true", dest="show_filenames") | ||
585 | parser.add_option("-i", "", | ||
586 | action="store", dest="ignore_layers", default="") | ||
587 | |||
588 | options, args = parser.parse_args(sys.argv) | ||
589 | ignore_layers = options.ignore_layers.split(',') | ||
590 | |||
591 | self.init_bbhandler() | ||
592 | |||
593 | pkg_fn = self.bbhandler.cooker_data.pkg_fn | ||
594 | bbpath = str(self.bbhandler.config_data.getVar('BBPATH', True)) | ||
595 | self.require_re = re.compile(r"require\s+(.+)") | ||
596 | self.include_re = re.compile(r"include\s+(.+)") | ||
597 | self.inherit_re = re.compile(r"inherit\s+(.+)") | ||
598 | |||
599 | global_inherit = (self.bbhandler.config_data.getVar('INHERIT', True) or "").split() | ||
600 | |||
601 | # The bb's DEPENDS and RDEPENDS | ||
602 | for f in pkg_fn: | ||
603 | f = bb.cache.Cache.virtualfn2realfn(f)[0] | ||
604 | # Get the layername that the file is in | ||
605 | layername = self.get_file_layer(f) | ||
606 | |||
607 | # The DEPENDS | ||
608 | deps = self.bbhandler.cooker_data.deps[f] | ||
609 | for pn in deps: | ||
610 | if pn in self.bbhandler.cooker_data.pkg_pn: | ||
611 | best = bb.providers.findBestProvider(pn, | ||
612 | self.bbhandler.config_data, | ||
613 | self.bbhandler.cooker_data, | ||
614 | self.bbhandler.cooker_data.pkg_pn) | ||
615 | self.check_cross_depends("DEPENDS", layername, f, best[3], options.show_filenames, ignore_layers) | ||
616 | |||
617 | # The RDPENDS | ||
618 | all_rdeps = self.bbhandler.cooker_data.rundeps[f].values() | ||
619 | # Remove the duplicated or null one. | ||
620 | sorted_rdeps = {} | ||
621 | # The all_rdeps is the list in list, so we need two for loops | ||
622 | for k1 in all_rdeps: | ||
623 | for k2 in k1: | ||
624 | sorted_rdeps[k2] = 1 | ||
625 | all_rdeps = sorted_rdeps.keys() | ||
626 | for rdep in all_rdeps: | ||
627 | all_p = bb.providers.getRuntimeProviders(self.bbhandler.cooker_data, rdep) | ||
628 | if all_p: | ||
629 | if f in all_p: | ||
630 | # The recipe provides this one itself, ignore | ||
631 | continue | ||
632 | best = bb.providers.filterProvidersRunTime(all_p, rdep, | ||
633 | self.bbhandler.config_data, | ||
634 | self.bbhandler.cooker_data)[0][0] | ||
635 | self.check_cross_depends("RDEPENDS", layername, f, best, options.show_filenames, ignore_layers) | ||
636 | |||
637 | # The RRECOMMENDS | ||
638 | all_rrecs = self.bbhandler.cooker_data.runrecs[f].values() | ||
639 | # Remove the duplicated or null one. | ||
640 | sorted_rrecs = {} | ||
641 | # The all_rrecs is the list in list, so we need two for loops | ||
642 | for k1 in all_rrecs: | ||
643 | for k2 in k1: | ||
644 | sorted_rrecs[k2] = 1 | ||
645 | all_rrecs = sorted_rrecs.keys() | ||
646 | for rrec in all_rrecs: | ||
647 | all_p = bb.providers.getRuntimeProviders(self.bbhandler.cooker_data, rrec) | ||
648 | if all_p: | ||
649 | if f in all_p: | ||
650 | # The recipe provides this one itself, ignore | ||
651 | continue | ||
652 | best = bb.providers.filterProvidersRunTime(all_p, rrec, | ||
653 | self.bbhandler.config_data, | ||
654 | self.bbhandler.cooker_data)[0][0] | ||
655 | self.check_cross_depends("RRECOMMENDS", layername, f, best, options.show_filenames, ignore_layers) | ||
656 | |||
657 | # The inherit class | ||
658 | cls_re = re.compile('classes/') | ||
659 | if f in self.bbhandler.cooker_data.inherits: | ||
660 | inherits = self.bbhandler.cooker_data.inherits[f] | ||
661 | for cls in inherits: | ||
662 | # The inherits' format is [classes/cls, /path/to/classes/cls] | ||
663 | # ignore the classes/cls. | ||
664 | if not cls_re.match(cls): | ||
665 | classname = os.path.splitext(os.path.basename(cls))[0] | ||
666 | if classname in global_inherit: | ||
667 | continue | ||
668 | inherit_layername = self.get_file_layer(cls) | ||
669 | if inherit_layername != layername and not inherit_layername in ignore_layers: | ||
670 | if not options.show_filenames: | ||
671 | f_short = self.remove_layer_prefix(f) | ||
672 | cls = self.remove_layer_prefix(cls) | ||
673 | else: | ||
674 | f_short = f | ||
675 | logger.plain("%s inherits %s" % (f_short, cls)) | ||
676 | |||
677 | # The 'require/include xxx' in the bb file | ||
678 | pv_re = re.compile(r"\${PV}") | ||
679 | fnfile = open(f, 'r') | ||
680 | line = fnfile.readline() | ||
681 | while line: | ||
682 | m, keyword = self.match_require_include(line) | ||
683 | # Found the 'require/include xxxx' | ||
684 | if m: | ||
685 | needed_file = m.group(1) | ||
686 | # Replace the ${PV} with the real PV | ||
687 | if pv_re.search(needed_file) and f in self.bbhandler.cooker_data.pkg_pepvpr: | ||
688 | pv = self.bbhandler.cooker_data.pkg_pepvpr[f][1] | ||
689 | needed_file = re.sub(r"\${PV}", pv, needed_file) | ||
690 | self.print_cross_files(bbpath, keyword, layername, f, needed_file, options.show_filenames, ignore_layers) | ||
691 | line = fnfile.readline() | ||
692 | fnfile.close() | ||
693 | |||
694 | # The "require/include xxx" in conf/machine/*.conf, .inc and .bbclass | ||
695 | conf_re = re.compile(".*/conf/machine/[^\/]*\.conf$") | ||
696 | inc_re = re.compile(".*\.inc$") | ||
697 | # The "inherit xxx" in .bbclass | ||
698 | bbclass_re = re.compile(".*\.bbclass$") | ||
699 | for layerdir in self.bblayers: | ||
700 | layername = self.get_layer_name(layerdir) | ||
701 | for dirpath, dirnames, filenames in os.walk(layerdir): | ||
702 | for name in filenames: | ||
703 | f = os.path.join(dirpath, name) | ||
704 | s = conf_re.match(f) or inc_re.match(f) or bbclass_re.match(f) | ||
705 | if s: | ||
706 | ffile = open(f, 'r') | ||
707 | line = ffile.readline() | ||
708 | while line: | ||
709 | m, keyword = self.match_require_include(line) | ||
710 | # Only bbclass has the "inherit xxx" here. | ||
711 | bbclass="" | ||
712 | if not m and f.endswith(".bbclass"): | ||
713 | m, keyword = self.match_inherit(line) | ||
714 | bbclass=".bbclass" | ||
715 | # Find a 'require/include xxxx' | ||
716 | if m: | ||
717 | self.print_cross_files(bbpath, keyword, layername, f, m.group(1) + bbclass, options.show_filenames, ignore_layers) | ||
718 | line = ffile.readline() | ||
719 | ffile.close() | ||
720 | |||
721 | def print_cross_files(self, bbpath, keyword, layername, f, needed_filename, show_filenames, ignore_layers): | ||
722 | """Print the depends that crosses a layer boundary""" | ||
723 | needed_file = bb.utils.which(bbpath, needed_filename) | ||
724 | if needed_file: | ||
725 | # Which layer is this file from | ||
726 | needed_layername = self.get_file_layer(needed_file) | ||
727 | if needed_layername != layername and not needed_layername in ignore_layers: | ||
728 | if not show_filenames: | ||
729 | f = self.remove_layer_prefix(f) | ||
730 | needed_file = self.remove_layer_prefix(needed_file) | ||
731 | logger.plain("%s %s %s" %(f, keyword, needed_file)) | ||
732 | |||
733 | def match_inherit(self, line): | ||
734 | """Match the inherit xxx line""" | ||
735 | return (self.inherit_re.match(line), "inherits") | ||
736 | |||
737 | def match_require_include(self, line): | ||
738 | """Match the require/include xxx line""" | ||
739 | m = self.require_re.match(line) | ||
740 | keyword = "requires" | ||
741 | if not m: | ||
742 | m = self.include_re.match(line) | ||
743 | keyword = "includes" | ||
744 | return (m, keyword) | ||
745 | |||
746 | def check_cross_depends(self, keyword, layername, f, needed_file, show_filenames, ignore_layers): | ||
747 | """Print the DEPENDS/RDEPENDS file that crosses a layer boundary""" | ||
748 | best_realfn = bb.cache.Cache.virtualfn2realfn(needed_file)[0] | ||
749 | needed_layername = self.get_file_layer(best_realfn) | ||
750 | if needed_layername != layername and not needed_layername in ignore_layers: | ||
751 | if not show_filenames: | ||
752 | f = self.remove_layer_prefix(f) | ||
753 | best_realfn = self.remove_layer_prefix(best_realfn) | ||
754 | |||
755 | logger.plain("%s %s %s" % (f, keyword, best_realfn)) | ||
756 | |||
757 | if __name__ == '__main__': | ||
758 | sys.exit(main(sys.argv[1:]) or 0) | ||