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